efl/legacy/evas/src/lib/cserve/evas_cs_server.c

381 lines
8.5 KiB
C

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "evas_cs.h"
#ifdef EVAS_CSERVE
EAPI Server *
evas_cserve_server_add(void)
{
Server *s;
char buf[PATH_MAX];
struct sockaddr_un socket_unix;
struct linger lin;
mode_t pmode;
int socket_unix_len;
s = calloc(1, sizeof(Server));
if (!s) return NULL;
s->fd = -1;
snprintf(buf, sizeof(buf), "/tmp/.evas-cserve-%x", getuid());
s->socket_path = strdup(buf);
if (!s->socket_path)
{
free(s);
return NULL;
}
pmode = umask(~(S_IRUSR | S_IWUSR));
start:
s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (s->fd < 0) goto error;
if (fcntl(s->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
if (fcntl(s->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
lin.l_onoff = 1;
lin.l_linger = 0;
if (setsockopt(s->fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0)
goto error;
socket_unix.sun_family = AF_UNIX;
strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
if (bind(s->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
{
if ((connect(s->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) &&
(unlink(s->socket_path) >= 0))
{
close(s->fd);
goto start;
}
else
goto error;
}
if (listen(s->fd, 4096) < 0) goto error;
umask(pmode);
return s;
error:
umask(pmode);
if (s->fd >= 0) close(s->fd);
free(s->socket_path);
free(s);
return NULL;
}
EAPI void
evas_cserve_server_del(Server *s)
{
Client *c;
EINA_LIST_FREE(s->clients, c)
{
LKL(c->lock);
close(c->fd);
if (c->buf) free(c->buf);
if (c->inbuf) free(c->inbuf);
LKD(c->lock);
free(c);
}
close(s->fd);
unlink(s->socket_path);
free(s->socket_path);
free(s);
}
static void
server_accept(Server *s)
{
Client *c;
int new_fd;
struct sockaddr_in incoming;
size_t size_in;
size_in = sizeof(struct sockaddr_in);
new_fd = accept(s->fd, (struct sockaddr *)&incoming, (socklen_t *)&size_in);
if (new_fd < 0) return;
fcntl(new_fd, F_SETFL, O_NONBLOCK);
fcntl(new_fd, F_SETFD, FD_CLOEXEC);
c = calloc(1, sizeof(Client));
if (!c)
{
close(new_fd);
return;
}
c->server = s;
c->fd = new_fd;
LKI(c->lock);
s->clients = eina_list_append(s->clients, c);
}
static void
client_flush(Client *c)
{
int num;
num = write(c->fd, c->buf, c->bufsize);
if (num < 0)
{
c->dead = 1;
return;
}
if (num < c->bufsize)
{
unsigned char *buf;
buf = malloc(c->bufsize - num);
if (buf)
{
memcpy(buf, c->buf + num, c->bufsize - num);
free(c->buf);
c->bufsize = c->bufsize - num;
c->bufalloc = c->bufsize;
c->buf = buf;
}
}
else
{
free(c->buf);
c->buf = NULL;
c->bufsize = 0;
c->bufalloc = 0;
}
}
static void
client_buf_add(Client *c, unsigned char *data, int size)
{
int newsize;
unsigned char *buf;
newsize = c->bufsize + size;
if (newsize > c->bufalloc)
{
c->bufalloc = newsize + 16384;
buf = realloc(c->buf, c->bufalloc);
if (buf) c->buf = buf;
else return;
}
memcpy(c->buf + c->bufsize, data, size);
c->bufsize += size;
}
static void
client_write(Client *c, unsigned char *data, int size)
{
int num;
if (!c->buf)
{
num = write(c->fd, data, size);
if (num != size)
client_buf_add(c, data + num, size - num);
}
else
{
client_buf_add(c, data, size);
}
}
EAPI void
evas_cserve_client_send(Client *c, int opcode, int size, unsigned char *data)
{
unsigned char *data2;
int *ints;
data2 = malloc(size + (sizeof(int) * 3));
if (!data2) return;
ints = (int *)data2;
ints[0] = size;
ints[1] = opcode;
// LKL(c->lock);
c->req_to++;
ints[2] = c->req_to;
memcpy(data2 + (sizeof(int) * 3), data, size);
client_write(c, data2, size + (sizeof(int) * 3));
// LKU(c->lock);
free(data2);
}
static void
server_message_handle(Server *s, Client *c, int opcode, int size, unsigned char *data)
{
if (s->func) s->func(s->data, s, c, opcode, size, data);
}
EAPI void
evas_cserve_server_message_handler_set(Server *s, int (*func) (void *fdata, Server *s, Client *c, int opcode, int size, unsigned char *data), void *data)
{
s->func = func;
s->data = data;
}
static int
server_parse(Server *s, Client *c)
{
int *ints;
unsigned char *data, *newbuf;
if (c->inbufsize < sizeof(int)) return 0;
ints = (int *)((c->inbuf));
if ((ints[0] < 0) || (ints[0] > (1024 * 1024)))
return 0;
if (c->inbufsize < (ints[0] + (sizeof(int) * 3)))
{
return 0;
}
data = c->inbuf + (sizeof(int) * 3);
if (ints[2] != (c->req_from + 1))
{
printf("EEK! sequence number mismatch from client with pid: %i\n"
"---- num %i is not 1 more than %i\n"
,
c->pid, ints[2], c->req_from);
return 0;
}
c->req_from++;
server_message_handle(s, c, ints[1], ints[0], data);
c->inbufalloc -= ints[0] + (sizeof(int) * 3);
if (c->inbufalloc == 0)
{
free(c->inbuf);
c->inbuf = NULL;
c->inbufsize = 0;
return 0;
}
newbuf = malloc(c->inbufalloc);
if (!newbuf)
{
c->inbufalloc += ints[0] + (sizeof(int) * 3);
/* fixme - bad situation */
return 0;
}
memcpy(newbuf, c->inbuf + ints[0] + (sizeof(int) * 3), c->inbufalloc);
c->inbufsize -= ints[0] + (sizeof(int) * 3);
free(c->inbuf);
c->inbuf = newbuf;
return 1;
}
static void
server_data(Server *s, Client *c, unsigned char *data, int size)
{
if (!c->inbuf)
{
c->inbuf = malloc(size);
if (c->inbuf)
{
memcpy(c->inbuf, data, size);
c->inbufalloc = size;
c->inbufsize = size;
}
else
{
/* fixme - bad situation */
return;
}
}
else
{
int size2;
size2 = c->inbufsize + size;
if (size2 > c->inbufalloc)
{
unsigned char *newbuf;
c->inbufalloc = size2;
newbuf = realloc(c->inbuf, c->inbufalloc);
if (newbuf) c->inbuf = newbuf;
else size2 = 0;
}
if (size2 > 0)
{
memcpy(c->inbuf + c->inbufsize, data, size);
c->inbufsize = size2;
}
else
{
/* fixme - bad situation */
return;
}
}
while (server_parse(s, c));
}
EAPI void
evas_cserve_server_wait(Server *s, int timeout)
{
fd_set rset, wset, xset;
int maxfd;
int ret;
struct timeval to;
Eina_List *l, *dead = NULL;
Client *c;
maxfd = 0;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&xset);
FD_SET(s->fd, &rset);
if (s->fd > maxfd) maxfd = s->fd;
EINA_LIST_FOREACH(s->clients, l, c)
{
FD_SET(c->fd, &rset);
if (c->buf)
FD_SET(c->fd, &wset);
if (c->fd > maxfd) maxfd = c->fd;
}
if (timeout > 0)
{
to.tv_sec = timeout / 1000000;
to.tv_usec = timeout % 1000000;
ret = select(maxfd + 1, &rset, &wset, &xset, &to);
}
else
ret = select(maxfd + 1, &rset, &wset, &xset, NULL);
if (ret < 1) return;
EINA_LIST_FOREACH(s->clients, l, c)
{
if (c->dead) continue;
if (FD_ISSET(c->fd, &rset))
{
char buf[16384];
int num;
errno = 0;
num = read(c->fd, buf, sizeof(buf));
if (num <= 0)
{
c->dead = 1;
dead = eina_list_append(dead, c);
}
else if (num > 0)
{
server_data(s, c, buf, num);
}
}
else if (FD_ISSET(c->fd, &wset))
{
client_flush(c);
if (c->dead) dead = eina_list_append(dead, c);
}
}
if (FD_ISSET(s->fd, &rset))
{
server_accept(s);
}
EINA_LIST_FREE(dead, c)
{
LKL(c->lock);
if (c->func) c->func(c->data, c);
s->clients = eina_list_remove(s->clients, c);
close(c->fd);
if (c->buf) free(c->buf);
if (c->inbuf) free(c->inbuf);
LKD(c->lock);
free(c);
}
}
#endif