|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include <Eina.h>
|
|
|
|
|
|
|
|
#include "evas_cs2.h"
|
|
|
|
#include "evas_cs2_private.h"
|
|
|
|
#include "evas_cs2_utils.h"
|
|
|
|
|
|
|
|
#ifdef EVAS_CSERVE2
|
|
|
|
|
|
|
|
#define TIMEOUT 1000
|
|
|
|
#define USE_SHARED_INDEX 1
|
|
|
|
#define SHARED_INDEX_ADD_TO_HASH 1
|
|
|
|
#define HKEY_LOAD_OPTS_STR_LEN 215
|
|
|
|
#define SPECIAL_RID_INDEX_LIST ((unsigned int) 0xFFFFFF42)
|
|
|
|
|
|
|
|
typedef Eina_Bool (*Op_Callback)(void *data, const void *msg, int size);
|
|
|
|
|
|
|
|
static const Evas_Image_Load_Opts empty_lo = {
|
|
|
|
{ 0, 0, 0, 0 },
|
|
|
|
{
|
|
|
|
0, 0, 0, 0,
|
|
|
|
0, 0,
|
|
|
|
0,
|
|
|
|
0
|
|
|
|
},
|
|
|
|
0.0,
|
|
|
|
0, 0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
|
|
|
|
EINA_FALSE
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _File_Entry {
|
|
|
|
unsigned int file_id;
|
|
|
|
unsigned int server_file_id;
|
|
|
|
EINA_REFCOUNT;
|
|
|
|
Eina_Stringshare *hkey;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _Client_Request {
|
|
|
|
Msg_Base *msg;
|
|
|
|
int msg_size;
|
|
|
|
Op_Callback cb;
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _File_Entry File_Entry;
|
|
|
|
typedef struct _Client_Request Client_Request;
|
|
|
|
|
|
|
|
static int cserve2_init = 0;
|
|
|
|
static int socketfd = -1;
|
|
|
|
static int sr_size = 0;
|
|
|
|
static int sr_got = 0;
|
|
|
|
static char *sr_buf = NULL;
|
|
|
|
|
|
|
|
static unsigned int _rid_count = 0;
|
|
|
|
static unsigned int _file_id = 0;
|
|
|
|
static unsigned int _data_id = 0;
|
|
|
|
|
|
|
|
static Eina_List *_requests = NULL;
|
|
|
|
static Eina_Hash *_file_entries = NULL;
|
|
|
|
|
|
|
|
// Shared index table
|
|
|
|
static Index_Table _index;
|
|
|
|
static const char *_shared_string_get(int id);
|
|
|
|
static const char *_shared_string_safe_get(int id); // Do not allow remap during search
|
|
|
|
static Eina_Bool _string_index_refresh(void);
|
|
|
|
static int _server_index_list_set(Msg_Base *data, int size);
|
|
|
|
static const File_Data *_shared_file_data_get_by_id(unsigned int id);
|
|
|
|
static const Shm_Object *_shared_index_item_get_by_id(Shared_Index *si, int elemsize, unsigned int id, Eina_Bool safe);
|
|
|
|
static const File_Data *_shared_image_entry_file_data_find(Image_Entry *ie);
|
|
|
|
static const Image_Data *_shared_image_entry_image_data_find(Image_Entry *ie);
|
|
|
|
static const Font_Data *_shared_font_entry_data_find(Font_Entry *fe);
|
|
|
|
static Eina_Bool _shared_index_remap_check(Shared_Index *si, int elemsize);
|
|
|
|
|
|
|
|
static Eina_Bool _server_dispatch_until(unsigned int rid);
|
|
|
|
unsigned int _image_load_server_send(Image_Entry *ie);
|
|
|
|
static unsigned int _image_open_server_send(Image_Entry *ie);
|
|
|
|
static unsigned int _glyph_request_server_send(Font_Entry *fe, Font_Hint_Flags hints, Eina_Bool used);
|
|
|
|
|
|
|
|
#ifndef UNIX_PATH_MAX
|
|
|
|
#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)NULL)->sun_path)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline Eina_Bool
|
|
|
|
_memory_zero_cmp(void *data, size_t len)
|
|
|
|
{
|
|
|
|
const int *idata = data;
|
|
|
|
const char *cdata;
|
|
|
|
int remain;
|
|
|
|
|
|
|
|
if (!data || !len) return EINA_TRUE;
|
|
|
|
|
|
|
|
for (remain = len / sizeof(idata); remain > 0; --remain)
|
|
|
|
if (*idata++ != 0) return EINA_FALSE;
|
|
|
|
|
|
|
|
cdata = (const char*) idata;
|
|
|
|
for (remain = ((const char*) data + len) - cdata; remain > 0; --remain)
|
|
|
|
if (*cdata++ != 0) return EINA_FALSE;
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_file_entry_free(void *data)
|
|
|
|
{
|
|
|
|
File_Entry *fentry = data;
|
|
|
|
if (!fentry) return;
|
|
|
|
eina_stringshare_del(fentry->hkey);
|
|
|
|
free(fentry);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_socket_path_set(char *path)
|
|
|
|
{
|
|
|
|
char *env;
|
|
|
|
char buf[UNIX_PATH_MAX];
|
|
|
|
|
|
|
|
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
|
|
|
|
if (getuid() == geteuid())
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
env = getenv("EVAS_CSERVE2_SOCKET");
|
|
|
|
if (env && env[0])
|
|
|
|
{
|
|
|
|
eina_strlcpy(path, env, UNIX_PATH_MAX);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "/tmp/.evas-cserve2-%x.socket", (int)getuid());
|
|
|
|
/* FIXME: check we can actually create this socket */
|
|
|
|
strcpy(path, buf);
|
|
|
|
#if 0
|
|
|
|
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
|
|
|
|
if (getuid() == geteuid())
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
env = getenv("XDG_RUNTIME_DIR");
|
|
|
|
if (!env || !env[0])
|
|
|
|
{
|
|
|
|
env = getenv("HOME");
|
|
|
|
if (!env || !env[0])
|
|
|
|
{
|
|
|
|
env = getenv("TMPDIR");
|
|
|
|
if (!env || !env[0])
|
|
|
|
env = "/tmp";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%s/evas-cserve2-%x.socket", env, getuid());
|
|
|
|
/* FIXME: check we can actually create this socket */
|
|
|
|
strcpy(path, buf);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_server_connect(void)
|
|
|
|
{
|
|
|
|
int s, len;
|
|
|
|
struct sockaddr_un remote;
|
|
|
|
#ifdef HAVE_FCNTL
|
|
|
|
int flags;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
|
|
|
|
{
|
|
|
|
ERR("cserve2 socket creation failed with error [%d] %s", errno, strerror(errno));
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_FCNTL
|
|
|
|
flags = fcntl(s, F_GETFD);
|
|
|
|
flags |= FD_CLOEXEC;
|
|
|
|
if (fcntl(s, F_SETFD, flags) < 0) ERR("can't set CLOEXEC on fd");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
remote.sun_family = AF_UNIX;
|
|
|
|
_socket_path_set(remote.sun_path);
|
|
|
|
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
errno = 0;
|
|
|
|
if (connect(s, (struct sockaddr *)&remote, len) != -1) break;
|
|
|
|
if (errno == EACCES)
|
|
|
|
{
|
|
|
|
ERR("not authorized to connect to cserve2!");
|
|
|
|
close(s);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
ERR("cserve2 connect failed: [%d] %s. Retrying...", errno, strerror(errno));
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
usleep(10000);
|
|
|
|
if (errno == EINTR)
|
|
|
|
{
|
|
|
|
WRN("received interruption while trying to connect to cserve2!");
|
|
|
|
close(s);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Here we should identify the error, maybe signal the daemon manager
|
|
|
|
* that we need cserve2 to [re]start or just quit and return false.
|
|
|
|
* There probably should be a timeout of some sort also...
|
|
|
|
* -- jpeg
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_FCNTL
|
|
|
|
if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) ERR("can't set non-blocking fd");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
socketfd = s;
|
|
|
|
sr_size = 0;
|
|
|
|
|
|
|
|
DBG("connected to cserve2 server.");
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_server_disconnect(void)
|
|
|
|
{
|
|
|
|
if (socketfd != -1)
|
|
|
|
close(socketfd);
|
|
|
|
socketfd = -1;
|
|
|
|
sr_size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_request_answer_add(Msg_Base *msg, int size, Op_Callback cb, void *data)
|
|
|
|
{
|
|
|
|
Client_Request *cr = calloc(1, sizeof(*cr));
|
|
|
|
|
|
|
|
cr->msg = msg;
|
|
|
|
cr->msg_size = size;
|
|
|
|
cr->cb = cb;
|
|
|
|
cr->data = data;
|
|
|
|
|
|
|
|
_requests = eina_list_append(_requests, cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_server_safe_send(int fd, const void *data, int size)
|
|
|
|
{
|
|
|
|
int sent = 0;
|
|
|
|
ssize_t ret;
|
|
|
|
const char *msg = data;
|
|
|
|
|
|
|
|
while (sent < size)
|
|
|
|
{
|
|
|
|
ret = send(fd, msg + sent, size - sent, MSG_NOSIGNAL);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
if ((errno == EAGAIN) || (errno == EINTR))
|
|
|
|
continue;
|
|
|
|
ERR("send() failed with error %d %m", errno);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
sent += ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_request_resend(unsigned int rid)
|
|
|
|
{
|
|
|
|
Eina_List *l;
|
|
|
|
Client_Request *cr;
|
|
|
|
Eina_Bool found = EINA_FALSE;
|
|
|
|
|
|
|
|
DBG("Re-sending %d requests...", eina_list_count(_requests));
|
|
|
|
EINA_LIST_FOREACH(_requests, l, cr)
|
|
|
|
{
|
|
|
|
if (rid)
|
|
|
|
{
|
|
|
|
if (cr->msg->rid != rid)
|
|
|
|
continue;
|
|
|
|
found = EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG("Sending request %d again: type %d", cr->msg->rid, cr->msg->type);
|
|
|
|
|
|
|
|
if (!_server_safe_send(socketfd, &cr->msg_size, sizeof(cr->msg_size)))
|
|
|
|
return EINA_FALSE;
|
|
|
|
if (!_server_safe_send(socketfd, cr->msg, cr->msg_size))
|
|
|
|
return EINA_FALSE;
|
|
|
|
|
|
|
|
if (found) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rid)
|
|
|
|
return found;
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_shared_index_close(Shared_Index *si)
|
|
|
|
{
|
|
|
|
if (!si) return;
|
|
|
|
|
|
|
|
if (si->f)
|
|
|
|
{
|
|
|
|
if (si->data)
|
|
|
|
eina_file_map_free(si->f, si->data);
|
|
|
|
eina_file_close(si->f);
|
|
|
|
}
|
|
|
|
if (si->entries_by_hkey)
|
|
|
|
eina_hash_free(si->entries_by_hkey);
|
|
|
|
memset(si, 0, sizeof(Shared_Index));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_shared_index_close_all()
|
|
|
|
{
|
|
|
|
DBG("Closing all index files");
|
|
|
|
if (_index.strings_entries.f)
|
|
|
|
{
|
|
|
|
if (_index.strings_entries.data)
|
|
|
|
eina_file_map_free(_index.strings_entries.f, _index.strings_entries.data);
|
|
|
|
eina_file_close(_index.strings_entries.f);
|
|
|
|
_index.strings_entries.data = NULL;
|
|
|
|
_index.strings_entries.f = NULL;
|
|
|
|
}
|
|
|
|
_shared_index_close(&_index.strings_index);
|
|
|
|
_shared_index_close(&_index.files);
|
|
|
|
_shared_index_close(&_index.images);
|
|
|
|
_shared_index_close(&_index.fonts);
|
|
|
|
_index.generation_id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_server_reconnect()
|
|
|
|
{
|
|
|
|
_shared_index_close_all();
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
_server_disconnect();
|
|
|
|
if (!_server_connect())
|
|
|
|
goto on_error;
|
|
|
|
|
|
|
|
if (!_server_dispatch_until(SPECIAL_RID_INDEX_LIST))
|
|
|
|
goto on_error;
|
|
|
|
|
|
|
|
/* NOTE: (TODO?)
|
|
|
|
* Either we reopen all images & fonts now
|
|
|
|
* Or we wait until new data is required again to request cserve2 to load
|
|
|
|
* it for us. Not sure which approch is the best now.
|
|
|
|
* So, for the moment, we'll just wait until the client needs new data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!_request_resend(0))
|
|
|
|
goto on_error;
|
|
|
|
|
|
|
|
INF("Successfully reconnected to cserve2");
|
|
|
|
return EINA_TRUE;
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
ERR("Unable to reconnect to server: %d %m", errno);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_request_answer_required(int type, Eina_Bool *valid)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case CSERVE2_OPEN:
|
|
|
|
case CSERVE2_LOAD:
|
|
|
|
case CSERVE2_PRELOAD:
|
|
|
|
case CSERVE2_FONT_LOAD:
|
|
|
|
case CSERVE2_FONT_GLYPHS_LOAD:
|
|
|
|
if (valid) *valid = EINA_TRUE;
|
|
|
|
return EINA_TRUE;
|
|
|
|
case CSERVE2_CLOSE:
|
|
|
|
case CSERVE2_UNLOAD:
|
|
|
|
case CSERVE2_FONT_UNLOAD:
|
|
|
|
case CSERVE2_FONT_GLYPHS_USED:
|
|
|
|
if (valid) *valid = EINA_TRUE;
|
|
|
|
return EINA_FALSE;
|
|
|
|
default:
|
|
|
|
ERR("Invalid message type %d", type);
|
|
|
|
if (valid) *valid = EINA_FALSE;
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_server_send(void *buf, int size, Op_Callback cb, void *data)
|
|
|
|
{
|
|
|
|
Msg_Base *msg = buf;
|
|
|
|
int type = msg->type;
|
|
|
|
Eina_Bool valid = EINA_TRUE;
|
|
|
|
|
|
|
|
if (!_server_safe_send(socketfd, &size, sizeof(size)))
|
|
|
|
{
|
|
|
|
ERR("Couldn't send message size to server.");
|
|
|
|
goto on_error;
|
|
|
|
}
|
|
|
|
if (!_server_safe_send(socketfd, buf, size))
|
|
|
|
{
|
|
|
|
ERR("Couldn't send message body to server.");
|
|
|
|
goto on_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_request_answer_required(type, &valid))
|
|
|
|
_request_answer_add(msg, size, cb, data);
|
|
|
|
else
|
|
|
|
free(msg);
|
|
|
|
|
|
|
|
return valid;
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
if (!_request_answer_required(type, NULL))
|
|
|
|
{
|
|
|
|
free(buf);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ERR("Socket error: %d %m", errno);
|
|
|
|
switch (errno)
|
|
|
|
{
|
|
|
|
case EPIPE:
|
|
|
|
case EBADF:
|
|
|
|
WRN("Trying to reconnect to server...");
|
|
|
|
if (!_server_reconnect())
|
|
|
|
{
|
|
|
|
free(buf);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
return _server_send(buf, size, cb, data);
|
|
|
|
default:
|
|
|
|
ERR("Can not recover from this error!");
|
|
|
|
free(buf);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
_server_read(int *size)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
void *ret;
|
|
|
|
|
|
|
|
if (socketfd < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (sr_size)
|
|
|
|
goto get_data;
|
|
|
|
|
|
|
|
n = recv(socketfd, &sr_size, sizeof(sr_size), 0);
|
|
|
|
if (n < 0)
|
|
|
|
return NULL;
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
DBG("Socket connection closed by server.");
|
|
|
|
_server_disconnect();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sr_buf = malloc(sr_size);
|
|
|
|
|
|
|
|
get_data:
|
|
|
|
n = recv(socketfd, sr_buf + sr_got, sr_size - sr_got, 0);
|
|
|
|
if (n < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sr_got += n;
|
|
|
|
if (sr_got < sr_size)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
*size = sr_size;
|
|
|
|
sr_size = 0;
|
|
|
|
sr_got = 0;
|
|
|
|
ret = sr_buf;
|
|
|
|
sr_buf = NULL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
evas_cserve2_init(void)
|
|
|
|
{
|
|
|
|
if (cserve2_init++)
|
|
|
|
return cserve2_init;
|
|
|
|
|
|
|
|
memset(&_index, 0, sizeof(_index));
|
|
|
|
|
|
|
|
DBG("Connecting to cserve2.");
|
|
|
|
if (!_server_connect())
|
|
|
|
{
|
|
|
|
cserve2_init = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_file_entries = eina_hash_string_superfast_new(EINA_FREE_CB(_file_entry_free));
|
|
|
|
return cserve2_init;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
evas_cserve2_shutdown(void)
|
|
|
|
{
|
|
|
|
const char zeros[sizeof(Msg_Index_List)] = {0};
|
|
|
|
Msg_Index_List *empty = (Msg_Index_List *) zeros;
|
|
|
|
|
|
|
|
if (cserve2_init <= 0)
|
|
|
|
{
|
|
|
|
CRI("cserve2 is already shutdown");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((--cserve2_init) > 0)
|
|
|
|
return cserve2_init;
|
|
|
|
|
|
|
|
DBG("Disconnecting from cserve2.");
|
|
|
|
empty->base.type = CSERVE2_INDEX_LIST;
|
|
|
|
_server_index_list_set((Msg_Base *) empty, sizeof(Msg_Index_List));
|
|
|
|
_server_disconnect();
|
|
|
|
|
|
|
|
eina_hash_free(_file_entries);
|
|
|
|
_file_entries = NULL;
|
|
|
|
|
|
|
|
return cserve2_init;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
evas_cserve2_use_get(void)
|
|
|
|
{
|
|
|
|
return cserve2_init;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
_next_rid(void)
|
|
|
|
{
|
|
|
|
if (!_rid_count)
|
|
|
|
_rid_count++;
|
|
|
|
|
|
|
|
return _rid_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
_server_dispatch(Eina_Bool *failed)
|
|
|
|
{
|
|
|
|
int size;
|
|
|
|
unsigned int rid;
|
|
|
|
Eina_List *l, *l_next;
|
|
|
|
Client_Request *cr;
|
|
|
|
Msg_Base *msg;
|
|
|
|
Eina_Bool found = EINA_FALSE;
|
|
|
|
|
|
|
|
msg = _server_read(&size);
|
|
|
|
if (!msg)
|
|
|
|
{
|
|
|
|
*failed = EINA_TRUE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*failed = EINA_FALSE;
|
|
|
|
|
|
|
|
// Special messages (no request)
|
|
|
|
switch (msg->type)
|
|
|
|
{
|
|
|
|
case CSERVE2_INDEX_LIST:
|
|
|
|
_server_index_list_set(msg, size);
|
|
|
|
free(msg);
|
|
|
|
return SPECIAL_RID_INDEX_LIST;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normal client to server requests
|
|
|
|
EINA_LIST_FOREACH_SAFE(_requests, l, l_next, cr)
|
|
|
|
{
|
|
|
|
Eina_Bool remove = EINA_TRUE;
|
|
|
|
|
|
|
|
if (cr->msg->rid != msg->rid) // dispatch this answer
|
|
|
|
continue;
|
|
|
|
|
|
|
|
found = EINA_TRUE;
|
|
|
|
if (cr->cb)
|
|
|
|
remove = cr->cb(cr->data, msg, size);
|
|
|
|
if (remove)
|
|
|
|
{
|
|
|
|
_requests = eina_list_remove_list(_requests, l);
|
|
|
|
free(cr->msg);
|
|
|
|
free(cr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rid = msg->rid;
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
if (msg->type == CSERVE2_ERROR)
|
|
|
|
{
|
|
|
|
Msg_Error *error = (Msg_Error *) msg;
|
|
|
|
ERR("Cserve2 sent error %d for rid %d", error->error, rid);
|
|
|
|
}
|
|
|
|
else WRN("Got unexpected response %d for request %d", msg->type, rid);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(msg);
|
|
|
|
return rid;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Eina_Bool
|
|
|
|
_server_dispatch_until(unsigned int rid)
|
|
|
|
{
|
|
|
|
Eina_Bool failed;
|
|
|
|
unsigned int rrid;
|
|
|
|
sigset_t sigmask;
|
|
|
|
|
|
|
|
// We want to block some signals from interrupting pselect().
|
|
|
|
// If the kernel implements TIF_RESTORE_SIGMASK, the
|
|
|
|
// signal handlers should be called right after pselect
|
|
|
|
// SIGCHLD: apps can have children that just terminated
|
|
|
|
sigprocmask(0, NULL, &sigmask);
|
|
|
|
sigaddset(&sigmask, SIGCHLD);
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
rrid = _server_dispatch(&failed);
|
|
|
|
if (rrid == rid) break;
|
|
|
|
#if TIMEOUT
|
|
|
|
else if (failed)
|
|
|
|
{
|
|
|
|
fd_set rfds;
|
|
|
|
struct timespec ts;
|
|
|
|
int sel;
|
|
|
|
|
|
|
|
if (socketfd == -1)
|
|
|
|
{
|
|
|
|
DBG("Reconnecting to server...");
|
|
|
|
if (!_server_connect())
|
|
|
|
{
|
|
|
|
ERR("Could not reconnect to cserve2!");
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
if (socketfd == -1) return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//DBG("Waiting for request %d...", rid);
|
|
|
|
FD_ZERO(&rfds);
|
|
|
|
FD_SET(socketfd, &rfds);
|
|
|
|
ts.tv_sec = TIMEOUT / 1000;
|
|
|
|
ts.tv_nsec = (TIMEOUT % 1000) * 1000000;
|
|
|
|
sel = pselect(socketfd + 1, &rfds, NULL, NULL, &ts, &sigmask);
|
|
|
|
if (sel == -1)
|
|
|
|
{
|
|
|
|
ERR("select() failed: [%d] %s", errno, strerror(errno));
|
|
|
|
/* FIXME: Depending on the error, we should probably try to reconnect to the server.
|
|
|
|
* Or even ask to [re]start the daemon.
|
|
|
|
* Or maybe just return EINA_FALSE after some timeout?
|
|
|
|
* -- jpeg
|
|
|
|
*/
|
|
|
|
if (errno == EINTR)
|
|
|
|
{
|
|
|
|
/* FIXME: Actually we might want to cancel our request
|
|
|
|
* ONLY when we received a SIGINT, but at this point
|
|
|
|
* there is no way we can know which signal we got.
|
|
|
|
* So we assume SIGINT and abandon this request.
|
|
|
|
*/
|
|
|
|
DBG("giving up on request %d after interrupt", rid);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|