2012-05-03 14:01:31 -07:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "evas_cserve2.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
#include <sys/inotify.h>
|
|
|
|
#include <sys/signalfd.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <unistd.h>
|
2012-07-09 19:35:00 -07:00
|
|
|
#include <fcntl.h>
|
2012-05-03 14:01:31 -07:00
|
|
|
|
|
|
|
#define MAX_EPOLL_EVENTS 10
|
|
|
|
#define MAX_INCOMING_CONN 10
|
|
|
|
|
|
|
|
struct _Watch_Data
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
Fd_Flags flags;
|
|
|
|
Fd_Watch_Cb callback;
|
|
|
|
const void *user_data;
|
2012-06-27 12:01:16 -07:00
|
|
|
Eina_Bool deleted : 1;
|
2012-05-03 14:01:31 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _Watch_Data Watch_Data;
|
|
|
|
|
|
|
|
struct _Inotify_Data
|
|
|
|
{
|
|
|
|
EINA_INLIST;
|
|
|
|
const char *path;
|
|
|
|
int watchid;
|
|
|
|
File_Change_Cb cb;
|
|
|
|
const void *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _Inotify_Data Inotify_Data;
|
|
|
|
|
|
|
|
static int epoll_fd = -1;
|
|
|
|
static int signal_fd = -1;
|
|
|
|
static int socket_fd = -1;
|
|
|
|
static int inotify_fd = -1;
|
|
|
|
static struct sockaddr_un socket_local;
|
|
|
|
static Eina_Hash *watch_list;
|
2012-06-27 12:01:16 -07:00
|
|
|
static Eina_List *deleted_watch_list;
|
2012-05-03 14:01:31 -07:00
|
|
|
static Eina_Hash *inotify_path_hash;
|
|
|
|
static Eina_Hash *inotify_id_hash;
|
|
|
|
static Eina_Bool running;
|
|
|
|
static Eina_Bool terminate;
|
|
|
|
static int timeout = -1; // in miliseconds
|
|
|
|
static long timeout_time = 0; // in miliseconds
|
|
|
|
|
|
|
|
static Timeout_Cb timeout_func = NULL;
|
|
|
|
static Main_Loop_Child_Dead_Cb reap_children_func = NULL;
|
|
|
|
|
|
|
|
#ifndef UNIX_PATH_MAX
|
|
|
|
#define UNIX_PATH_MAX sizeof(socket_local.sun_path)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
2012-11-04 03:51:42 -08:00
|
|
|
_signal_handle_child(struct signalfd_siginfo *sinfo EINA_UNUSED)
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
while ((pid = waitpid(0, &status, WNOHANG)) > 0)
|
|
|
|
{
|
|
|
|
if (reap_children_func)
|
|
|
|
{
|
|
|
|
reap_children_func(pid, status);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG("Received SIGCHLD and no handler is set.");
|
|
|
|
|
|
|
|
if (WIFEXITED(status))
|
|
|
|
DBG("Child '%d' exited with status '%d'.", pid,
|
|
|
|
WEXITSTATUS(status));
|
|
|
|
else if (WIFSIGNALED(status))
|
|
|
|
DBG("Child '%d' exited with signal '%d'.", pid,
|
|
|
|
WTERMSIG(status));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-04 03:51:42 -08:00
|
|
|
_signal_handle_int(struct signalfd_siginfo *sinfo EINA_UNUSED)
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
DBG("Received SIGINT. Honoring request.");
|
|
|
|
terminate = EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-04 03:51:42 -08:00
|
|
|
_signal_handle_term(struct signalfd_siginfo *sinfo EINA_UNUSED)
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
DBG("Received SIGTERM. Honoring request.");
|
|
|
|
terminate = EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-04 03:51:42 -08:00
|
|
|
_signalfd_handler(int fd, Fd_Flags flags EINA_UNUSED, void *data EINA_UNUSED)
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
struct signalfd_siginfo sinfo;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
ret = read(fd, &sinfo, sizeof(struct signalfd_siginfo));
|
|
|
|
if (ret == -1)
|
|
|
|
{
|
|
|
|
if (errno == EAGAIN)
|
|
|
|
break;
|
|
|
|
ERR("Error reading from signal fd: %m.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(sinfo.ssi_signo)
|
|
|
|
{
|
|
|
|
case SIGCHLD:
|
|
|
|
_signal_handle_child(&sinfo);
|
|
|
|
break;
|
|
|
|
case SIGINT:
|
|
|
|
_signal_handle_int(&sinfo);
|
|
|
|
break;
|
|
|
|
case SIGTERM:
|
|
|
|
_signal_handle_term(&sinfo);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERR("Caught unexpected signal '%d'.", sinfo.ssi_signo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_signalfd_setup(void)
|
|
|
|
{
|
|
|
|
sigset_t mask;
|
|
|
|
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGCHLD);
|
|
|
|
sigaddset(&mask, SIGINT);
|
|
|
|
sigaddset(&mask, SIGTERM);
|
|
|
|
|
|
|
|
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not set mask of handled signals.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
|
|
|
if (signal_fd == -1)
|
|
|
|
ERR("Could not create file descriptor from signalfd.");
|
|
|
|
|
|
|
|
/* ignore SIGPIPE so it's handled by write() and send() as needed */
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
|
|
|
return signal_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_signalfd_finish(void)
|
|
|
|
{
|
|
|
|
sigset_t mask;
|
|
|
|
|
|
|
|
cserve2_fd_watch_del(signal_fd);
|
|
|
|
|
|
|
|
sigemptyset(&mask);
|
|
|
|
sigprocmask(SIG_BLOCK, NULL, &mask);
|
|
|
|
|
|
|
|
close(signal_fd);
|
|
|
|
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
|
|
|
|
|
|
signal(SIGPIPE, SIG_DFL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-04 03:51:42 -08:00
|
|
|
_socketfd_handler(int fd EINA_UNUSED, Fd_Flags flags EINA_UNUSED, void *data EINA_UNUSED)
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
struct sockaddr_un remote;
|
|
|
|
unsigned int len;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
len = sizeof(struct sockaddr_un);
|
2012-07-09 19:35:00 -07:00
|
|
|
s = accept(socket_fd, &remote, &len);
|
2012-05-03 14:01:31 -07:00
|
|
|
if (s == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not accept socket: \"%s\"", strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
2012-07-09 22:18:00 -07:00
|
|
|
/* TODO: when porting to windows, do:
|
|
|
|
* SetHandleInformation(s, HANDLE_FLAG_INHERIT, 0)
|
|
|
|
*/
|
2012-07-09 19:35:00 -07:00
|
|
|
fcntl(s, F_SETFD, FD_CLOEXEC);
|
2012-05-03 14:01:31 -07:00
|
|
|
|
|
|
|
cserve2_client_accept(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_socket_path_set(char *path)
|
|
|
|
{
|
|
|
|
char *env;
|
|
|
|
char buf[UNIX_PATH_MAX];
|
|
|
|
|
|
|
|
env = getenv("EVAS_CSERVE2_SOCKET");
|
|
|
|
if (env && env[0])
|
|
|
|
{
|
|
|
|
strncpy(path, env, UNIX_PATH_MAX - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-21 01:44:55 -07:00
|
|
|
snprintf(buf, sizeof(buf), "/tmp/.evas-cserve2-%x.socket", (int)getuid());
|
|
|
|
/* FIXME: check we can actually create this socket */
|
|
|
|
strcpy(path, buf);
|
|
|
|
return;
|
|
|
|
#if 0
|
2012-05-03 14:01:31 -07:00
|
|
|
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);
|
2012-05-21 01:44:55 -07:00
|
|
|
#endif
|
2012-05-03 14:01:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_socketfd_setup(void)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
|
|
|
if (s == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not create socketfd: \"%s\"", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
socket_local.sun_family = AF_UNIX;
|
|
|
|
_socket_path_set(socket_local.sun_path);
|
|
|
|
DBG("Using '%s' as server socket.", socket_local.sun_path);
|
|
|
|
unlink(socket_local.sun_path);
|
|
|
|
len = strlen(socket_local.sun_path) + sizeof(socket_local.sun_family);
|
|
|
|
if (bind(s, (struct sockaddr *)&socket_local, len) == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not bind socketfd: \"%s\"", strerror(errno));
|
|
|
|
close(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (listen(s, MAX_INCOMING_CONN) == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not listen on socketfd: \"%s\"", strerror(errno));
|
|
|
|
close(s);
|
|
|
|
unlink(socket_local.sun_path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
socket_fd = s;
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_socketfd_finish(void)
|
|
|
|
{
|
|
|
|
close(socket_fd);
|
|
|
|
unlink(socket_local.sun_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-11-04 03:51:42 -08:00
|
|
|
_inotifyfd_handler(int fd, Fd_Flags flags, void *data EINA_UNUSED)
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
char buffer[16384];
|
|
|
|
int i = 0;
|
|
|
|
ssize_t size;
|
|
|
|
|
|
|
|
if (flags & FD_ERROR)
|
|
|
|
{
|
|
|
|
ERR("Error on inotify file handler, what to do?");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = read(fd, buffer, sizeof(buffer));
|
|
|
|
while (i < size)
|
|
|
|
{
|
|
|
|
struct inotify_event *event;
|
|
|
|
int event_size;
|
|
|
|
Eina_Inlist *ids, *itr;
|
|
|
|
Inotify_Data *id;
|
|
|
|
Eina_Bool deleted;
|
|
|
|
|
|
|
|
event = (struct inotify_event *)&buffer[i];
|
|
|
|
event_size = sizeof(struct inotify_event) + event->len;
|
|
|
|
i += event_size;
|
|
|
|
|
|
|
|
ids = eina_hash_find(inotify_id_hash, &event->wd);
|
|
|
|
if (!ids) continue;
|
|
|
|
|
|
|
|
deleted = !!(event->mask
|
|
|
|
& (IN_DELETE_SELF | IN_MOVE_SELF
|
|
|
|
| IN_IGNORED | IN_UNMOUNT));
|
|
|
|
EINA_INLIST_FOREACH_SAFE(ids, itr, id)
|
|
|
|
id->cb(id->path, deleted, (void *)id->data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_inotify_id_hash_free_cb(void *data)
|
|
|
|
{
|
|
|
|
Eina_Inlist *list = data;
|
|
|
|
|
|
|
|
while (list)
|
|
|
|
{
|
|
|
|
Inotify_Data *id;
|
|
|
|
id = EINA_INLIST_CONTAINER_GET(list, Inotify_Data);
|
|
|
|
list = eina_inlist_remove(list, list);
|
|
|
|
eina_stringshare_del(id->path);
|
|
|
|
free(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
_inotifyfd_setup(void)
|
|
|
|
{
|
|
|
|
inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
|
|
|
if (inotify_fd == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not create inotifyfd: \"%s\"", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
inotify_path_hash = eina_hash_string_superfast_new(NULL);
|
|
|
|
inotify_id_hash = eina_hash_int32_new(_inotify_id_hash_free_cb);
|
|
|
|
|
|
|
|
return inotify_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_inotifyfd_finish(void)
|
|
|
|
{
|
|
|
|
close(inotify_fd);
|
|
|
|
|
|
|
|
eina_hash_free(inotify_path_hash);
|
|
|
|
eina_hash_free(inotify_id_hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_watch_data_free_cb(void *data)
|
|
|
|
{
|
2012-06-27 12:01:16 -07:00
|
|
|
Watch_Data *wd = data;
|
|
|
|
wd->deleted = EINA_TRUE;
|
|
|
|
deleted_watch_list = eina_list_append(deleted_watch_list, wd);
|
2012-05-03 14:01:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
cserve2_main_loop_setup(void)
|
|
|
|
{
|
|
|
|
int sfd;
|
2012-05-14 08:05:04 -07:00
|
|
|
int sock;
|
2012-05-03 14:01:31 -07:00
|
|
|
int ifd;
|
|
|
|
|
|
|
|
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
|
|
|
|
|
|
|
if (epoll_fd < 0)
|
|
|
|
{
|
|
|
|
ERR("Could not create epoll fd.");
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
watch_list = eina_hash_int32_new(_watch_data_free_cb);
|
|
|
|
if (!watch_list)
|
|
|
|
{
|
|
|
|
ERR("Could not create watch list hash struct.");
|
|
|
|
close(epoll_fd);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
sfd = _signalfd_setup();
|
|
|
|
if (sfd == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not setup signalfd.");
|
|
|
|
close(epoll_fd);
|
|
|
|
eina_hash_free(watch_list);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG("Add watch for signal fd: %d", sfd);
|
|
|
|
if (!cserve2_fd_watch_add(sfd, FD_READ, _signalfd_handler, NULL))
|
|
|
|
{
|
|
|
|
ERR("Could not add watch for signalfd.");
|
|
|
|
close(sfd);
|
|
|
|
close(epoll_fd);
|
|
|
|
eina_hash_free(watch_list);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
2012-05-14 08:05:04 -07:00
|
|
|
sock = _socketfd_setup();
|
|
|
|
if (sock == -1)
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
ERR("Could not setup socketfd.");
|
|
|
|
goto error_socket;
|
|
|
|
}
|
|
|
|
|
2012-05-14 08:05:04 -07:00
|
|
|
DBG("Add watch for socket fd: %d", sock);
|
|
|
|
if (!cserve2_fd_watch_add(sock, FD_READ, _socketfd_handler, NULL))
|
2012-05-03 14:01:31 -07:00
|
|
|
{
|
|
|
|
ERR("Could not add watch for socketf.");
|
2012-05-14 08:05:04 -07:00
|
|
|
close(sock);
|
2012-05-03 14:01:31 -07:00
|
|
|
goto error_socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
ifd = _inotifyfd_setup();
|
|
|
|
if (ifd == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not setup inotify.");
|
|
|
|
goto error_inotify;
|
|
|
|
}
|
|
|
|
|
|
|
|
DBG("Add watch for inotify fd: %d", ifd);
|
|
|
|
if (!cserve2_fd_watch_add(ifd, FD_READ, _inotifyfd_handler, NULL))
|
|
|
|
{
|
|
|
|
ERR("Could not add watch for inotifyfd.");
|
|
|
|
close(ifd);
|
|
|
|
goto error_inotify;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
|
|
|
|
error_inotify:
|
|
|
|
_socketfd_finish();
|
|
|
|
|
|
|
|
error_socket:
|
|
|
|
close(sfd);
|
|
|
|
close(epoll_fd);
|
|
|
|
eina_hash_free(watch_list);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cserve2_main_loop_finish(void)
|
|
|
|
{
|
2012-06-27 12:01:16 -07:00
|
|
|
Watch_Data *wd;
|
|
|
|
|
2012-05-03 14:01:31 -07:00
|
|
|
_socketfd_finish();
|
|
|
|
|
|
|
|
_signalfd_finish();
|
|
|
|
|
|
|
|
_inotifyfd_finish();
|
|
|
|
|
|
|
|
eina_hash_free(watch_list);
|
2012-06-27 12:01:16 -07:00
|
|
|
EINA_LIST_FREE(deleted_watch_list, wd)
|
|
|
|
free(wd);
|
2012-05-03 14:01:31 -07:00
|
|
|
|
|
|
|
close(epoll_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
cserve2_fd_watch_add(int fd, Fd_Flags flags, Fd_Watch_Cb cb, const void *data)
|
|
|
|
{
|
|
|
|
Watch_Data *w_data;
|
|
|
|
struct epoll_event ev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
DBG("Add watch for fd %d, flags = 0x%x", fd, flags);
|
|
|
|
|
|
|
|
if ((fd < 0) || (!cb))
|
|
|
|
{
|
|
|
|
ERR("Can't add watch: fd = %d, callback = %p", fd, cb);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
w_data = calloc(1, sizeof(*w_data));
|
|
|
|
w_data->fd = fd;
|
|
|
|
w_data->flags = flags;
|
|
|
|
w_data->callback = cb;
|
|
|
|
w_data->user_data = data;
|
|
|
|
|
|
|
|
if (!eina_hash_add(watch_list, &fd, w_data))
|
|
|
|
{
|
|
|
|
ERR("Could not add watch for fd %d to the hash.", fd);
|
|
|
|
free(w_data);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
if (flags & FD_READ)
|
|
|
|
ev.events |= EPOLLIN;
|
|
|
|
if (flags & FD_WRITE)
|
|
|
|
ev.events |= EPOLLOUT;
|
|
|
|
ev.data.ptr = w_data;
|
|
|
|
|
|
|
|
err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
|
|
|
|
if (!err < 0)
|
|
|
|
{
|
|
|
|
ERR("Could not create epoll watch for fd %d.", fd);
|
|
|
|
eina_hash_del(watch_list, &fd, NULL);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
cserve2_fd_watch_flags_set(int fd, Fd_Flags flags)
|
|
|
|
{
|
|
|
|
Watch_Data *w_data;
|
|
|
|
struct epoll_event ev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
DBG("Set watch flags for fd %d, flags = 0x%x", fd, flags);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
ERR("Can't modify watch: fd = %d", fd);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
w_data = eina_hash_find(watch_list, &fd);
|
|
|
|
if (!w_data)
|
|
|
|
{
|
|
|
|
ERR("Couldn't find data for fd %d: not being watched.", fd);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags == w_data->flags)
|
|
|
|
return EINA_TRUE;
|
|
|
|
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
if (flags & FD_READ)
|
|
|
|
ev.events |= EPOLLIN;
|
|
|
|
if (flags & FD_WRITE)
|
|
|
|
ev.events |= EPOLLOUT;
|
|
|
|
ev.data.ptr = w_data;
|
|
|
|
|
|
|
|
err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
|
|
|
|
if (err < 0)
|
|
|
|
{
|
|
|
|
ERR("Could not modify epoll watch for fd: %d", fd);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
cserve2_fd_watch_flags_get(int fd, Fd_Flags *flags)
|
|
|
|
{
|
|
|
|
Watch_Data *w_data;
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
ERR("Can't get flags for watch: fd = %d", fd);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
w_data = eina_hash_find(watch_list, &fd);
|
|
|
|
if (!w_data)
|
|
|
|
{
|
|
|
|
ERR("Couldn't find data for fd: %d. Is it really being watched?", fd);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*flags = w_data->flags;
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
cserve2_fd_watch_del(int fd)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
DBG("Remove watch for fd %d", fd);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return EINA_FALSE;
|
|
|
|
|
|
|
|
if (!eina_hash_del(watch_list, &fd, NULL))
|
|
|
|
ERR("Could not remove watch for fd %d from watch list hash.", fd);
|
|
|
|
|
|
|
|
err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
|
|
|
|
if (err < 0)
|
|
|
|
{
|
|
|
|
ERR("Could not remove epoll watch for fd %d.", fd);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
cserve2_file_change_watch_add(const char *path, File_Change_Cb cb, const void *data)
|
|
|
|
{
|
|
|
|
Inotify_Data *id;
|
|
|
|
Eina_Inlist *ids;
|
|
|
|
unsigned int mask = IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF;
|
|
|
|
|
|
|
|
id = eina_hash_find(inotify_path_hash, path);
|
|
|
|
if (id)
|
|
|
|
{
|
|
|
|
ERR("Attempt to watch changes for path '%s', which is already "
|
|
|
|
"being watched.", path);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
id = calloc(1, sizeof(Inotify_Data));
|
|
|
|
if (!id)
|
|
|
|
{
|
|
|
|
ERR("Could not alloc Inotify_Data instance.");
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
id->watchid = inotify_add_watch(inotify_fd, path, mask);
|
|
|
|
if (id->watchid == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not add inotify watch for %s: %m.", path);
|
|
|
|
free(id);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
id->path = eina_stringshare_add(path);
|
|
|
|
id->cb = cb;
|
|
|
|
id->data = data;
|
|
|
|
|
|
|
|
eina_hash_direct_add(inotify_path_hash, id->path, id);
|
|
|
|
|
|
|
|
ids = eina_hash_find(inotify_id_hash, &id->watchid);
|
|
|
|
ids = eina_inlist_append(ids, EINA_INLIST_GET(id));
|
|
|
|
eina_hash_set(inotify_id_hash, &id->watchid, ids);
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Eina_Bool
|
|
|
|
cserve2_file_change_watch_del(const char *path)
|
|
|
|
{
|
|
|
|
Inotify_Data *id;
|
|
|
|
Eina_Inlist *ids;
|
|
|
|
int wd;
|
|
|
|
|
|
|
|
id = eina_hash_set(inotify_path_hash, path, NULL);
|
|
|
|
if (!id)
|
|
|
|
{
|
|
|
|
ERR("Requested to remove change watch for %s, but it's not being "
|
|
|
|
"watched.", path);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ids = eina_hash_find(inotify_id_hash, &id->watchid);
|
|
|
|
ids = eina_inlist_remove(ids, EINA_INLIST_GET(id));
|
|
|
|
eina_hash_set(inotify_id_hash, &id->watchid, ids);
|
|
|
|
|
|
|
|
wd = id->watchid;
|
|
|
|
eina_stringshare_del(id->path);
|
|
|
|
free(id);
|
|
|
|
|
|
|
|
if (!ids)
|
|
|
|
{
|
|
|
|
if (inotify_rm_watch(inotify_fd, wd) == -1)
|
|
|
|
{
|
|
|
|
ERR("Could not remove change watch for %s: %m", path);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return EINA_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_update_timeout(void)
|
|
|
|
{
|
|
|
|
struct timeval timev;
|
|
|
|
long cur_time;
|
|
|
|
|
|
|
|
if (timeout <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
gettimeofday(&timev, NULL);
|
|
|
|
cur_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
|
|
|
|
timeout -= cur_time - timeout_time;
|
|
|
|
timeout_time = cur_time;
|
|
|
|
|
|
|
|
if (timeout < 0)
|
|
|
|
timeout = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cserve2_timeout_cb_set(int t, Timeout_Cb cb)
|
|
|
|
{
|
|
|
|
struct timeval timev;
|
|
|
|
if (cb && t <= 0)
|
|
|
|
{
|
|
|
|
ERR("timeout must be a value greater than 0 to set a callback."
|
|
|
|
" given timeout: %d miliseconds", t);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cb)
|
|
|
|
{
|
|
|
|
DBG("Removing timeout callback.");
|
|
|
|
timeout = -1;
|
|
|
|
cb = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//DBG("Setting timeout to: %d miliseconds", t);
|
|
|
|
gettimeofday(&timev, NULL);
|
|
|
|
timeout_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
|
|
|
|
timeout = t;
|
|
|
|
timeout_func = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cserve2_on_child_dead_set(Main_Loop_Child_Dead_Cb cb)
|
|
|
|
{
|
|
|
|
reap_children_func = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cserve2_main_loop_run(void)
|
|
|
|
{
|
|
|
|
running = EINA_TRUE;
|
|
|
|
terminate = EINA_FALSE;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
struct epoll_event events[MAX_EPOLL_EVENTS];
|
|
|
|
int n, nfds;
|
2012-06-27 12:01:16 -07:00
|
|
|
Watch_Data *data;
|
2012-05-03 14:01:31 -07:00
|
|
|
|
|
|
|
if (terminate)
|
|
|
|
break;
|
|
|
|
|
|
|
|
nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout);
|
|
|
|
if (nfds < 0)
|
|
|
|
{
|
|
|
|
ERR("An error occurred when reading the epoll fd.");
|
|
|
|
ERR("%s", strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (nfds == 0) // timeout occurred
|
|
|
|
{
|
|
|
|
timeout = -1;
|
|
|
|
if (!timeout_func)
|
|
|
|
ERR("Timeout expired, but no timeout function set.");
|
|
|
|
else
|
|
|
|
timeout_func();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (n = 0; n < nfds; n++)
|
|
|
|
{
|
2012-06-27 12:01:16 -07:00
|
|
|
data = events[n].data.ptr;
|
2012-05-03 14:01:31 -07:00
|
|
|
Fd_Flags flags = 0;
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
continue;
|
|
|
|
|
2012-06-27 12:01:16 -07:00
|
|
|
if (data->deleted)
|
|
|
|
continue;
|
|
|
|
|
2012-05-03 14:01:31 -07:00
|
|
|
if (!data->callback)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (events[n].events & EPOLLIN)
|
|
|
|
flags |= FD_READ;
|
|
|
|
if (events[n].events & EPOLLOUT)
|
|
|
|
flags |= FD_WRITE;
|
|
|
|
if (events[n].events & EPOLLERR)
|
|
|
|
flags |= FD_ERROR;
|
|
|
|
data->callback(data->fd, flags, (void *)data->user_data);
|
|
|
|
}
|
|
|
|
|
2012-06-27 12:01:16 -07:00
|
|
|
EINA_LIST_FREE(deleted_watch_list, data)
|
|
|
|
free(data);
|
|
|
|
|
2012-05-03 14:01:31 -07:00
|
|
|
_update_timeout();
|
|
|
|
}
|
|
|
|
|
|
|
|
running = EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
cserve2_client_read(Client *client, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
return recv(client->socket, buf, len, MSG_DONTWAIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
|
|
|
cserve2_client_write(Client *client, const void *data, size_t size)
|
|
|
|
{
|
|
|
|
return send(client->socket, data, size, MSG_NOSIGNAL | MSG_DONTWAIT);
|
|
|
|
}
|