5 #include "evas_cserve2.h"
10 #include <sys/inotify.h>
11 #include <sys/signalfd.h>
12 #include <sys/socket.h>
14 #include <sys/types.h>
20 #define MAX_EPOLL_EVENTS 10
21 #define MAX_INCOMING_CONN 10
28 const void *user_data;
31 typedef struct _Watch_Data Watch_Data;
42 typedef struct _Inotify_Data Inotify_Data;
44 static int epoll_fd = -1;
45 static int signal_fd = -1;
46 static int socket_fd = -1;
47 static int inotify_fd = -1;
48 static struct sockaddr_un socket_local;
49 static Eina_Hash *watch_list;
50 static Eina_Hash *inotify_path_hash;
51 static Eina_Hash *inotify_id_hash;
52 static Eina_Bool running;
53 static Eina_Bool terminate;
54 static int timeout = -1; // in miliseconds
55 static long timeout_time = 0; // in miliseconds
57 static Timeout_Cb timeout_func = NULL;
58 static Main_Loop_Child_Dead_Cb reap_children_func = NULL;
61 #define UNIX_PATH_MAX sizeof(socket_local.sun_path)
65 _signal_handle_child(struct signalfd_siginfo *sinfo __UNUSED__)
70 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
72 if (reap_children_func)
74 reap_children_func(pid, status);
78 DBG("Received SIGCHLD and no handler is set.");
80 if (WIFEXITED(status))
81 DBG("Child '%d' exited with status '%d'.", pid,
83 else if (WIFSIGNALED(status))
84 DBG("Child '%d' exited with signal '%d'.", pid,
90 _signal_handle_int(struct signalfd_siginfo *sinfo __UNUSED__)
92 DBG("Received SIGINT. Honoring request.");
93 terminate = EINA_TRUE;
97 _signal_handle_term(struct signalfd_siginfo *sinfo __UNUSED__)
99 DBG("Received SIGTERM. Honoring request.");
100 terminate = EINA_TRUE;
104 _signalfd_handler(int fd, Fd_Flags flags __UNUSED__, void *data __UNUSED__)
106 struct signalfd_siginfo sinfo;
111 ret = read(fd, &sinfo, sizeof(struct signalfd_siginfo));
116 ERR("Error reading from signal fd: %m.");
120 switch(sinfo.ssi_signo)
123 _signal_handle_child(&sinfo);
126 _signal_handle_int(&sinfo);
129 _signal_handle_term(&sinfo);
132 ERR("Caught unexpected signal '%d'.", sinfo.ssi_signo);
138 _signalfd_setup(void)
143 sigaddset(&mask, SIGCHLD);
144 sigaddset(&mask, SIGINT);
145 sigaddset(&mask, SIGTERM);
147 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
149 ERR("Could not set mask of handled signals.");
153 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
155 ERR("Could not create file descriptor from signalfd.");
157 /* ignore SIGPIPE so it's handled by write() and send() as needed */
158 signal(SIGPIPE, SIG_IGN);
164 _signalfd_finish(void)
168 cserve2_fd_watch_del(signal_fd);
171 sigprocmask(SIG_BLOCK, NULL, &mask);
174 sigprocmask(SIG_UNBLOCK, &mask, NULL);
176 signal(SIGPIPE, SIG_DFL);
180 _socketfd_handler(int fd __UNUSED__, Fd_Flags flags __UNUSED__, void *data __UNUSED__)
182 struct sockaddr_un remote;
186 len = sizeof(struct sockaddr_un);
187 s = accept4(socket_fd, &remote, &len, SOCK_CLOEXEC);
190 ERR("Could not accept socket: \"%s\"", strerror(errno));
194 cserve2_client_accept(s);
198 _socket_path_set(char *path)
201 char buf[UNIX_PATH_MAX];
203 env = getenv("EVAS_CSERVE2_SOCKET");
206 strncpy(path, env, UNIX_PATH_MAX - 1);
210 snprintf(buf, sizeof(buf), "/tmp/.evas-cserve2-%x.socket", (int)getuid());
211 /* FIXME: check we can actually create this socket */
215 env = getenv("XDG_RUNTIME_DIR");
218 env = getenv("HOME");
221 env = getenv("TMPDIR");
227 snprintf(buf, sizeof(buf), "%s/evas-cserve2-%x.socket", env, getuid());
228 /* FIXME: check we can actually create this socket */
234 _socketfd_setup(void)
239 s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
242 ERR("Could not create socketfd: \"%s\"", strerror(errno));
246 socket_local.sun_family = AF_UNIX;
247 _socket_path_set(socket_local.sun_path);
248 DBG("Using '%s' as server socket.", socket_local.sun_path);
249 unlink(socket_local.sun_path);
250 len = strlen(socket_local.sun_path) + sizeof(socket_local.sun_family);
251 if (bind(s, (struct sockaddr *)&socket_local, len) == -1)
253 ERR("Could not bind socketfd: \"%s\"", strerror(errno));
258 if (listen(s, MAX_INCOMING_CONN) == -1)
260 ERR("Could not listen on socketfd: \"%s\"", strerror(errno));
262 unlink(socket_local.sun_path);
272 _socketfd_finish(void)
275 unlink(socket_local.sun_path);
279 _inotifyfd_handler(int fd, Fd_Flags flags, void *data __UNUSED__)
285 if (flags & FD_ERROR)
287 ERR("Error on inotify file handler, what to do?");
291 size = read(fd, buffer, sizeof(buffer));
294 struct inotify_event *event;
296 Eina_Inlist *ids, *itr;
300 event = (struct inotify_event *)&buffer[i];
301 event_size = sizeof(struct inotify_event) + event->len;
304 ids = eina_hash_find(inotify_id_hash, &event->wd);
307 deleted = !!(event->mask
308 & (IN_DELETE_SELF | IN_MOVE_SELF
309 | IN_IGNORED | IN_UNMOUNT));
310 EINA_INLIST_FOREACH_SAFE(ids, itr, id)
311 id->cb(id->path, deleted, (void *)id->data);
316 _inotify_id_hash_free_cb(void *data)
318 Eina_Inlist *list = data;
323 id = EINA_INLIST_CONTAINER_GET(list, Inotify_Data);
324 list = eina_inlist_remove(list, list);
325 eina_stringshare_del(id->path);
331 _inotifyfd_setup(void)
333 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
334 if (inotify_fd == -1)
336 ERR("Could not create inotifyfd: \"%s\"", strerror(errno));
339 inotify_path_hash = eina_hash_string_superfast_new(NULL);
340 inotify_id_hash = eina_hash_int32_new(_inotify_id_hash_free_cb);
346 _inotifyfd_finish(void)
350 eina_hash_free(inotify_path_hash);
351 eina_hash_free(inotify_id_hash);
355 _watch_data_free_cb(void *data)
361 cserve2_main_loop_setup(void)
367 epoll_fd = epoll_create1(EPOLL_CLOEXEC);
371 ERR("Could not create epoll fd.");
375 watch_list = eina_hash_int32_new(_watch_data_free_cb);
378 ERR("Could not create watch list hash struct.");
383 sfd = _signalfd_setup();
386 ERR("Could not setup signalfd.");
388 eina_hash_free(watch_list);
392 DBG("Add watch for signal fd: %d", sfd);
393 if (!cserve2_fd_watch_add(sfd, FD_READ, _signalfd_handler, NULL))
395 ERR("Could not add watch for signalfd.");
398 eina_hash_free(watch_list);
402 sock = _socketfd_setup();
405 ERR("Could not setup socketfd.");
409 DBG("Add watch for socket fd: %d", sock);
410 if (!cserve2_fd_watch_add(sock, FD_READ, _socketfd_handler, NULL))
412 ERR("Could not add watch for socketf.");
417 ifd = _inotifyfd_setup();
420 ERR("Could not setup inotify.");
424 DBG("Add watch for inotify fd: %d", ifd);
425 if (!cserve2_fd_watch_add(ifd, FD_READ, _inotifyfd_handler, NULL))
427 ERR("Could not add watch for inotifyfd.");
440 eina_hash_free(watch_list);
445 cserve2_main_loop_finish(void)
453 eina_hash_free(watch_list);
459 cserve2_fd_watch_add(int fd, Fd_Flags flags, Fd_Watch_Cb cb, const void *data)
462 struct epoll_event ev;
465 DBG("Add watch for fd %d, flags = 0x%x", fd, flags);
467 if ((fd < 0) || (!cb))
469 ERR("Can't add watch: fd = %d, callback = %p", fd, cb);
473 w_data = calloc(1, sizeof(*w_data));
475 w_data->flags = flags;
476 w_data->callback = cb;
477 w_data->user_data = data;
479 if (!eina_hash_add(watch_list, &fd, w_data))
481 ERR("Could not add watch for fd %d to the hash.", fd);
486 memset(&ev, 0, sizeof(ev));
488 ev.events |= EPOLLIN;
489 if (flags & FD_WRITE)
490 ev.events |= EPOLLOUT;
491 ev.data.ptr = w_data;
493 err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
496 ERR("Could not create epoll watch for fd %d.", fd);
497 eina_hash_del(watch_list, &fd, NULL);
505 cserve2_fd_watch_flags_set(int fd, Fd_Flags flags)
508 struct epoll_event ev;
511 DBG("Set watch flags for fd %d, flags = 0x%x", fd, flags);
515 ERR("Can't modify watch: fd = %d", fd);
519 w_data = eina_hash_find(watch_list, &fd);
522 ERR("Couldn't find data for fd %d: not being watched.", fd);
526 if (flags == w_data->flags)
529 memset(&ev, 0, sizeof(ev));
531 ev.events |= EPOLLIN;
532 if (flags & FD_WRITE)
533 ev.events |= EPOLLOUT;
534 ev.data.ptr = w_data;
536 err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
539 ERR("Could not modify epoll watch for fd: %d", fd);
547 cserve2_fd_watch_flags_get(int fd, Fd_Flags *flags)
552 ERR("Can't get flags for watch: fd = %d", fd);
556 w_data = eina_hash_find(watch_list, &fd);
559 ERR("Couldn't find data for fd: %d. Is it really being watched?", fd);
563 *flags = w_data->flags;
569 cserve2_fd_watch_del(int fd)
573 DBG("Remove watch for fd %d", fd);
578 if (!eina_hash_del(watch_list, &fd, NULL))
579 ERR("Could not remove watch for fd %d from watch list hash.", fd);
581 err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
584 ERR("Could not remove epoll watch for fd %d.", fd);
592 cserve2_file_change_watch_add(const char *path, File_Change_Cb cb, const void *data)
596 unsigned int mask = IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF;
598 id = eina_hash_find(inotify_path_hash, path);
601 ERR("Attempt to watch changes for path '%s', which is already "
602 "being watched.", path);
606 id = calloc(1, sizeof(Inotify_Data));
609 ERR("Could not alloc Inotify_Data instance.");
613 id->watchid = inotify_add_watch(inotify_fd, path, mask);
614 if (id->watchid == -1)
616 ERR("Could not add inotify watch for %s: %m.", path);
621 id->path = eina_stringshare_add(path);
625 eina_hash_direct_add(inotify_path_hash, id->path, id);
627 ids = eina_hash_find(inotify_id_hash, &id->watchid);
628 ids = eina_inlist_append(ids, EINA_INLIST_GET(id));
629 eina_hash_set(inotify_id_hash, &id->watchid, ids);
635 cserve2_file_change_watch_del(const char *path)
641 id = eina_hash_set(inotify_path_hash, path, NULL);
644 ERR("Requested to remove change watch for %s, but it's not being "
649 ids = eina_hash_find(inotify_id_hash, &id->watchid);
650 ids = eina_inlist_remove(ids, EINA_INLIST_GET(id));
651 eina_hash_set(inotify_id_hash, &id->watchid, ids);
654 eina_stringshare_del(id->path);
659 if (inotify_rm_watch(inotify_fd, wd) == -1)
661 ERR("Could not remove change watch for %s: %m", path);
670 _update_timeout(void)
672 struct timeval timev;
678 gettimeofday(&timev, NULL);
679 cur_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
680 timeout -= cur_time - timeout_time;
681 timeout_time = cur_time;
688 cserve2_timeout_cb_set(int t, Timeout_Cb cb)
690 struct timeval timev;
693 ERR("timeout must be a value greater than 0 to set a callback."
694 " given timeout: %d miliseconds", t);
700 DBG("Removing timeout callback.");
706 //DBG("Setting timeout to: %d miliseconds", t);
707 gettimeofday(&timev, NULL);
708 timeout_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
714 cserve2_on_child_dead_set(Main_Loop_Child_Dead_Cb cb)
716 reap_children_func = cb;
720 cserve2_main_loop_run(void)
723 terminate = EINA_FALSE;
727 struct epoll_event events[MAX_EPOLL_EVENTS];
733 nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout);
736 ERR("An error occurred when reading the epoll fd.");
737 ERR("%s", strerror(errno));
740 if (nfds == 0) // timeout occurred
744 ERR("Timeout expired, but no timeout function set.");
749 for (n = 0; n < nfds; n++)
751 Watch_Data *data = events[n].data.ptr;
760 if (events[n].events & EPOLLIN)
762 if (events[n].events & EPOLLOUT)
764 if (events[n].events & EPOLLERR)
766 data->callback(data->fd, flags, (void *)data->user_data);
772 running = EINA_FALSE;
776 cserve2_client_read(Client *client, void *buf, size_t len)
778 return recv(client->socket, buf, len, MSG_DONTWAIT);
782 cserve2_client_write(Client *client, const void *data, size_t size)
784 return send(client->socket, data, size, MSG_NOSIGNAL | MSG_DONTWAIT);