summaryrefslogtreecommitdiff
path: root/src/bin/evas/evas_cserve2_main_loop_linux.c
diff options
context:
space:
mode:
authorVincent Torri <vincent.torri@gmail.com>2012-11-04 11:51:42 +0000
committerVincent Torri <vincent.torri@gmail.com>2012-11-04 11:51:42 +0000
commitc15e9c6575c3b5f39ded167dda5259de3de96151 (patch)
tree5115d7ae3620af24c2bc094cd062575af7adeda9 /src/bin/evas/evas_cserve2_main_loop_linux.c
parenta5ac6a987caec5a7f7596a25d0a065b9cc94c50c (diff)
merge: and now Evas
I've tested make -j 3 install and it works nicely I've tested expedite with software and opengl xlib, and it works. Not tested other engines, so please report any problems (engines or other) on the ML. TODO: examples and tests, I'll add them later ISSUE: Eina_Unicode size check. It indirectly depends on eina_config.h, which is created at the end of the configure script. So its size is always 0. I don't know how that size is used, so I can't do a lot, for now. SVN revision: 78895
Diffstat (limited to 'src/bin/evas/evas_cserve2_main_loop_linux.c')
-rw-r--r--src/bin/evas/evas_cserve2_main_loop_linux.c805
1 files changed, 805 insertions, 0 deletions
diff --git a/src/bin/evas/evas_cserve2_main_loop_linux.c b/src/bin/evas/evas_cserve2_main_loop_linux.c
new file mode 100644
index 0000000000..afd678ee3e
--- /dev/null
+++ b/src/bin/evas/evas_cserve2_main_loop_linux.c
@@ -0,0 +1,805 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include "evas_cserve2.h"
6
7#include <errno.h>
8#include <string.h>
9#include <sys/epoll.h>
10#include <sys/inotify.h>
11#include <sys/signalfd.h>
12#include <sys/socket.h>
13#include <sys/time.h>
14#include <sys/types.h>
15#include <sys/un.h>
16#include <sys/wait.h>
17#include <signal.h>
18#include <unistd.h>
19#include <fcntl.h>
20
21#define MAX_EPOLL_EVENTS 10
22#define MAX_INCOMING_CONN 10
23
24struct _Watch_Data
25{
26 int fd;
27 Fd_Flags flags;
28 Fd_Watch_Cb callback;
29 const void *user_data;
30 Eina_Bool deleted : 1;
31};
32
33typedef struct _Watch_Data Watch_Data;
34
35struct _Inotify_Data
36{
37 EINA_INLIST;
38 const char *path;
39 int watchid;
40 File_Change_Cb cb;
41 const void *data;
42};
43
44typedef struct _Inotify_Data Inotify_Data;
45
46static int epoll_fd = -1;
47static int signal_fd = -1;
48static int socket_fd = -1;
49static int inotify_fd = -1;
50static struct sockaddr_un socket_local;
51static Eina_Hash *watch_list;
52static Eina_List *deleted_watch_list;
53static Eina_Hash *inotify_path_hash;
54static Eina_Hash *inotify_id_hash;
55static Eina_Bool running;
56static Eina_Bool terminate;
57static int timeout = -1; // in miliseconds
58static long timeout_time = 0; // in miliseconds
59
60static Timeout_Cb timeout_func = NULL;
61static Main_Loop_Child_Dead_Cb reap_children_func = NULL;
62
63#ifndef UNIX_PATH_MAX
64#define UNIX_PATH_MAX sizeof(socket_local.sun_path)
65#endif
66
67static void
68_signal_handle_child(struct signalfd_siginfo *sinfo EINA_UNUSED)
69{
70 int status;
71 pid_t pid;
72
73 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
74 {
75 if (reap_children_func)
76 {
77 reap_children_func(pid, status);
78 continue;
79 }
80
81 DBG("Received SIGCHLD and no handler is set.");
82
83 if (WIFEXITED(status))
84 DBG("Child '%d' exited with status '%d'.", pid,
85 WEXITSTATUS(status));
86 else if (WIFSIGNALED(status))
87 DBG("Child '%d' exited with signal '%d'.", pid,
88 WTERMSIG(status));
89 }
90}
91
92static void
93_signal_handle_int(struct signalfd_siginfo *sinfo EINA_UNUSED)
94{
95 DBG("Received SIGINT. Honoring request.");
96 terminate = EINA_TRUE;
97}
98
99static void
100_signal_handle_term(struct signalfd_siginfo *sinfo EINA_UNUSED)
101{
102 DBG("Received SIGTERM. Honoring request.");
103 terminate = EINA_TRUE;
104}
105
106static void
107_signalfd_handler(int fd, Fd_Flags flags EINA_UNUSED, void *data EINA_UNUSED)
108{
109 struct signalfd_siginfo sinfo;
110 ssize_t ret;
111
112 for (;;)
113 {
114 ret = read(fd, &sinfo, sizeof(struct signalfd_siginfo));
115 if (ret == -1)
116 {
117 if (errno == EAGAIN)
118 break;
119 ERR("Error reading from signal fd: %m.");
120 return;
121 }
122
123 switch(sinfo.ssi_signo)
124 {
125 case SIGCHLD:
126 _signal_handle_child(&sinfo);
127 break;
128 case SIGINT:
129 _signal_handle_int(&sinfo);
130 break;
131 case SIGTERM:
132 _signal_handle_term(&sinfo);
133 break;
134 default:
135 ERR("Caught unexpected signal '%d'.", sinfo.ssi_signo);
136 }
137 }
138}
139
140static int
141_signalfd_setup(void)
142{
143 sigset_t mask;
144
145 sigemptyset(&mask);
146 sigaddset(&mask, SIGCHLD);
147 sigaddset(&mask, SIGINT);
148 sigaddset(&mask, SIGTERM);
149
150 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
151 {
152 ERR("Could not set mask of handled signals.");
153 return -1;
154 }
155
156 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
157 if (signal_fd == -1)
158 ERR("Could not create file descriptor from signalfd.");
159
160 /* ignore SIGPIPE so it's handled by write() and send() as needed */
161 signal(SIGPIPE, SIG_IGN);
162
163 return signal_fd;
164}
165
166static void
167_signalfd_finish(void)
168{
169 sigset_t mask;
170
171 cserve2_fd_watch_del(signal_fd);
172
173 sigemptyset(&mask);
174 sigprocmask(SIG_BLOCK, NULL, &mask);
175
176 close(signal_fd);
177 sigprocmask(SIG_UNBLOCK, &mask, NULL);
178
179 signal(SIGPIPE, SIG_DFL);
180}
181
182static void
183_socketfd_handler(int fd EINA_UNUSED, Fd_Flags flags EINA_UNUSED, void *data EINA_UNUSED)
184{
185 struct sockaddr_un remote;
186 unsigned int len;
187 int s;
188
189 len = sizeof(struct sockaddr_un);
190 s = accept(socket_fd, &remote, &len);
191 if (s == -1)
192 {
193 ERR("Could not accept socket: \"%s\"", strerror(errno));
194 return;
195 }
196 /* TODO: when porting to windows, do:
197 * SetHandleInformation(s, HANDLE_FLAG_INHERIT, 0)
198 */
199 fcntl(s, F_SETFD, FD_CLOEXEC);
200
201 cserve2_client_accept(s);
202}
203
204static void
205_socket_path_set(char *path)
206{
207 char *env;
208 char buf[UNIX_PATH_MAX];
209
210 env = getenv("EVAS_CSERVE2_SOCKET");
211 if (env && env[0])
212 {
213 strncpy(path, env, UNIX_PATH_MAX - 1);
214 return;
215 }
216
217 snprintf(buf, sizeof(buf), "/tmp/.evas-cserve2-%x.socket", (int)getuid());
218 /* FIXME: check we can actually create this socket */
219 strcpy(path, buf);
220 return;
221#if 0
222 env = getenv("XDG_RUNTIME_DIR");
223 if (!env || !env[0])
224 {
225 env = getenv("HOME");
226 if (!env || !env[0])
227 {
228 env = getenv("TMPDIR");
229 if (!env || !env[0])
230 env = "/tmp";
231 }
232 }
233
234 snprintf(buf, sizeof(buf), "%s/evas-cserve2-%x.socket", env, getuid());
235 /* FIXME: check we can actually create this socket */
236 strcpy(path, buf);
237#endif
238}
239
240static int
241_socketfd_setup(void)
242{
243 int s;
244 int len;
245
246 s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
247 if (s == -1)
248 {
249 ERR("Could not create socketfd: \"%s\"", strerror(errno));
250 return -1;
251 }
252
253 socket_local.sun_family = AF_UNIX;
254 _socket_path_set(socket_local.sun_path);
255 DBG("Using '%s' as server socket.", socket_local.sun_path);
256 unlink(socket_local.sun_path);
257 len = strlen(socket_local.sun_path) + sizeof(socket_local.sun_family);
258 if (bind(s, (struct sockaddr *)&socket_local, len) == -1)
259 {
260 ERR("Could not bind socketfd: \"%s\"", strerror(errno));
261 close(s);
262 return -1;
263 }
264
265 if (listen(s, MAX_INCOMING_CONN) == -1)
266 {
267 ERR("Could not listen on socketfd: \"%s\"", strerror(errno));
268 close(s);
269 unlink(socket_local.sun_path);
270 return -1;
271 }
272
273 socket_fd = s;
274
275 return s;
276}
277
278static void
279_socketfd_finish(void)
280{
281 close(socket_fd);
282 unlink(socket_local.sun_path);
283}
284
285static void
286_inotifyfd_handler(int fd, Fd_Flags flags, void *data EINA_UNUSED)
287{
288 char buffer[16384];
289 int i = 0;
290 ssize_t size;
291
292 if (flags & FD_ERROR)
293 {
294 ERR("Error on inotify file handler, what to do?");
295 return;
296 }
297
298 size = read(fd, buffer, sizeof(buffer));
299 while (i < size)
300 {
301 struct inotify_event *event;
302 int event_size;
303 Eina_Inlist *ids, *itr;
304 Inotify_Data *id;
305 Eina_Bool deleted;
306
307 event = (struct inotify_event *)&buffer[i];
308 event_size = sizeof(struct inotify_event) + event->len;
309 i += event_size;
310
311 ids = eina_hash_find(inotify_id_hash, &event->wd);
312 if (!ids) continue;
313
314 deleted = !!(event->mask
315 & (IN_DELETE_SELF | IN_MOVE_SELF
316 | IN_IGNORED | IN_UNMOUNT));
317 EINA_INLIST_FOREACH_SAFE(ids, itr, id)
318 id->cb(id->path, deleted, (void *)id->data);
319 }
320}
321
322static void
323_inotify_id_hash_free_cb(void *data)
324{
325 Eina_Inlist *list = data;
326
327 while (list)
328 {
329 Inotify_Data *id;
330 id = EINA_INLIST_CONTAINER_GET(list, Inotify_Data);
331 list = eina_inlist_remove(list, list);
332 eina_stringshare_del(id->path);
333 free(id);
334 }
335}
336
337static int
338_inotifyfd_setup(void)
339{
340 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
341 if (inotify_fd == -1)
342 {
343 ERR("Could not create inotifyfd: \"%s\"", strerror(errno));
344 return -1;
345 }
346 inotify_path_hash = eina_hash_string_superfast_new(NULL);
347 inotify_id_hash = eina_hash_int32_new(_inotify_id_hash_free_cb);
348
349 return inotify_fd;
350}
351
352static void
353_inotifyfd_finish(void)
354{
355 close(inotify_fd);
356
357 eina_hash_free(inotify_path_hash);
358 eina_hash_free(inotify_id_hash);
359}
360
361static void
362_watch_data_free_cb(void *data)
363{
364 Watch_Data *wd = data;
365 wd->deleted = EINA_TRUE;
366 deleted_watch_list = eina_list_append(deleted_watch_list, wd);
367}
368
369Eina_Bool
370cserve2_main_loop_setup(void)
371{
372 int sfd;
373 int sock;
374 int ifd;
375
376 epoll_fd = epoll_create1(EPOLL_CLOEXEC);
377
378 if (epoll_fd < 0)
379 {
380 ERR("Could not create epoll fd.");
381 return EINA_FALSE;
382 }
383
384 watch_list = eina_hash_int32_new(_watch_data_free_cb);
385 if (!watch_list)
386 {
387 ERR("Could not create watch list hash struct.");
388 close(epoll_fd);
389 return EINA_FALSE;
390 }
391
392 sfd = _signalfd_setup();
393 if (sfd == -1)
394 {
395 ERR("Could not setup signalfd.");
396 close(epoll_fd);
397 eina_hash_free(watch_list);
398 return EINA_FALSE;
399 }
400
401 DBG("Add watch for signal fd: %d", sfd);
402 if (!cserve2_fd_watch_add(sfd, FD_READ, _signalfd_handler, NULL))
403 {
404 ERR("Could not add watch for signalfd.");
405 close(sfd);
406 close(epoll_fd);
407 eina_hash_free(watch_list);
408 return EINA_FALSE;
409 }
410
411 sock = _socketfd_setup();
412 if (sock == -1)
413 {
414 ERR("Could not setup socketfd.");
415 goto error_socket;
416 }
417
418 DBG("Add watch for socket fd: %d", sock);
419 if (!cserve2_fd_watch_add(sock, FD_READ, _socketfd_handler, NULL))
420 {
421 ERR("Could not add watch for socketf.");
422 close(sock);
423 goto error_socket;
424 }
425
426 ifd = _inotifyfd_setup();
427 if (ifd == -1)
428 {
429 ERR("Could not setup inotify.");
430 goto error_inotify;
431 }
432
433 DBG("Add watch for inotify fd: %d", ifd);
434 if (!cserve2_fd_watch_add(ifd, FD_READ, _inotifyfd_handler, NULL))
435 {
436 ERR("Could not add watch for inotifyfd.");
437 close(ifd);
438 goto error_inotify;
439 }
440
441 return EINA_TRUE;
442
443error_inotify:
444 _socketfd_finish();
445
446error_socket:
447 close(sfd);
448 close(epoll_fd);
449 eina_hash_free(watch_list);
450 return EINA_FALSE;
451}
452
453void
454cserve2_main_loop_finish(void)
455{
456 Watch_Data *wd;
457
458 _socketfd_finish();
459
460 _signalfd_finish();
461
462 _inotifyfd_finish();
463
464 eina_hash_free(watch_list);
465 EINA_LIST_FREE(deleted_watch_list, wd)
466 free(wd);
467
468 close(epoll_fd);
469}
470
471Eina_Bool
472cserve2_fd_watch_add(int fd, Fd_Flags flags, Fd_Watch_Cb cb, const void *data)
473{
474 Watch_Data *w_data;
475 struct epoll_event ev;
476 int err;
477
478 DBG("Add watch for fd %d, flags = 0x%x", fd, flags);
479
480 if ((fd < 0) || (!cb))
481 {
482 ERR("Can't add watch: fd = %d, callback = %p", fd, cb);
483 return EINA_FALSE;
484 }
485
486 w_data = calloc(1, sizeof(*w_data));
487 w_data->fd = fd;
488 w_data->flags = flags;
489 w_data->callback = cb;
490 w_data->user_data = data;
491
492 if (!eina_hash_add(watch_list, &fd, w_data))
493 {
494 ERR("Could not add watch for fd %d to the hash.", fd);
495 free(w_data);
496 return EINA_FALSE;
497 }
498
499 memset(&ev, 0, sizeof(ev));
500 if (flags & FD_READ)
501 ev.events |= EPOLLIN;
502 if (flags & FD_WRITE)
503 ev.events |= EPOLLOUT;
504 ev.data.ptr = w_data;
505
506 err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
507 if (!err < 0)
508 {
509 ERR("Could not create epoll watch for fd %d.", fd);
510 eina_hash_del(watch_list, &fd, NULL);
511 return EINA_FALSE;
512 }
513
514 return EINA_TRUE;
515}
516
517Eina_Bool
518cserve2_fd_watch_flags_set(int fd, Fd_Flags flags)
519{
520 Watch_Data *w_data;
521 struct epoll_event ev;
522 int err;
523
524 DBG("Set watch flags for fd %d, flags = 0x%x", fd, flags);
525
526 if (fd < 0)
527 {
528 ERR("Can't modify watch: fd = %d", fd);
529 return EINA_FALSE;
530 }
531
532 w_data = eina_hash_find(watch_list, &fd);
533 if (!w_data)
534 {
535 ERR("Couldn't find data for fd %d: not being watched.", fd);
536 return EINA_FALSE;
537 }
538
539 if (flags == w_data->flags)
540 return EINA_TRUE;
541
542 memset(&ev, 0, sizeof(ev));
543 if (flags & FD_READ)
544 ev.events |= EPOLLIN;
545 if (flags & FD_WRITE)
546 ev.events |= EPOLLOUT;
547 ev.data.ptr = w_data;
548
549 err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
550 if (err < 0)
551 {
552 ERR("Could not modify epoll watch for fd: %d", fd);
553 return EINA_FALSE;
554 }
555
556 return EINA_TRUE;
557}
558
559Eina_Bool
560cserve2_fd_watch_flags_get(int fd, Fd_Flags *flags)
561{
562 Watch_Data *w_data;
563 if (fd < 0)
564 {
565 ERR("Can't get flags for watch: fd = %d", fd);
566 return EINA_FALSE;
567 }
568
569 w_data = eina_hash_find(watch_list, &fd);
570 if (!w_data)
571 {
572 ERR("Couldn't find data for fd: %d. Is it really being watched?", fd);
573 return EINA_FALSE;
574 }
575
576 *flags = w_data->flags;
577
578 return EINA_TRUE;
579}
580
581Eina_Bool
582cserve2_fd_watch_del(int fd)
583{
584 int err;
585
586 DBG("Remove watch for fd %d", fd);
587
588 if (fd < 0)
589 return EINA_FALSE;
590
591 if (!eina_hash_del(watch_list, &fd, NULL))
592 ERR("Could not remove watch for fd %d from watch list hash.", fd);
593
594 err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
595 if (err < 0)
596 {
597 ERR("Could not remove epoll watch for fd %d.", fd);
598 return EINA_FALSE;
599 }
600
601 return EINA_TRUE;
602}
603
604Eina_Bool
605cserve2_file_change_watch_add(const char *path, File_Change_Cb cb, const void *data)
606{
607 Inotify_Data *id;
608 Eina_Inlist *ids;
609 unsigned int mask = IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF;
610
611 id = eina_hash_find(inotify_path_hash, path);
612 if (id)
613 {
614 ERR("Attempt to watch changes for path '%s', which is already "
615 "being watched.", path);
616 return EINA_FALSE;
617 }
618
619 id = calloc(1, sizeof(Inotify_Data));
620 if (!id)
621 {
622 ERR("Could not alloc Inotify_Data instance.");
623 return EINA_FALSE;
624 }
625
626 id->watchid = inotify_add_watch(inotify_fd, path, mask);
627 if (id->watchid == -1)
628 {
629 ERR("Could not add inotify watch for %s: %m.", path);
630 free(id);
631 return EINA_FALSE;
632 }
633
634 id->path = eina_stringshare_add(path);
635 id->cb = cb;
636 id->data = data;
637
638 eina_hash_direct_add(inotify_path_hash, id->path, id);
639
640 ids = eina_hash_find(inotify_id_hash, &id->watchid);
641 ids = eina_inlist_append(ids, EINA_INLIST_GET(id));
642 eina_hash_set(inotify_id_hash, &id->watchid, ids);
643
644 return EINA_TRUE;
645}
646
647Eina_Bool
648cserve2_file_change_watch_del(const char *path)
649{
650 Inotify_Data *id;
651 Eina_Inlist *ids;
652 int wd;
653
654 id = eina_hash_set(inotify_path_hash, path, NULL);
655 if (!id)
656 {
657 ERR("Requested to remove change watch for %s, but it's not being "
658 "watched.", path);
659 return EINA_FALSE;
660 }
661
662 ids = eina_hash_find(inotify_id_hash, &id->watchid);
663 ids = eina_inlist_remove(ids, EINA_INLIST_GET(id));
664 eina_hash_set(inotify_id_hash, &id->watchid, ids);
665
666 wd = id->watchid;
667 eina_stringshare_del(id->path);
668 free(id);
669
670 if (!ids)
671 {
672 if (inotify_rm_watch(inotify_fd, wd) == -1)
673 {
674 ERR("Could not remove change watch for %s: %m", path);
675 return EINA_FALSE;
676 }
677 }
678
679 return EINA_TRUE;
680}
681
682static void
683_update_timeout(void)
684{
685 struct timeval timev;
686 long cur_time;
687
688 if (timeout <= 0)
689 return;
690
691 gettimeofday(&timev, NULL);
692 cur_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
693 timeout -= cur_time - timeout_time;
694 timeout_time = cur_time;
695
696 if (timeout < 0)
697 timeout = 0;
698}
699
700void
701cserve2_timeout_cb_set(int t, Timeout_Cb cb)
702{
703 struct timeval timev;
704 if (cb && t <= 0)
705 {
706 ERR("timeout must be a value greater than 0 to set a callback."
707 " given timeout: %d miliseconds", t);
708 return;
709 }
710
711 if (!cb)
712 {
713 DBG("Removing timeout callback.");
714 timeout = -1;
715 cb = NULL;
716 return;
717 }
718
719 //DBG("Setting timeout to: %d miliseconds", t);
720 gettimeofday(&timev, NULL);
721 timeout_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
722 timeout = t;
723 timeout_func = cb;
724}
725
726void
727cserve2_on_child_dead_set(Main_Loop_Child_Dead_Cb cb)
728{
729 reap_children_func = cb;
730}
731
732void
733cserve2_main_loop_run(void)
734{
735 running = EINA_TRUE;
736 terminate = EINA_FALSE;
737
738 for (;;)
739 {
740 struct epoll_event events[MAX_EPOLL_EVENTS];
741 int n, nfds;
742 Watch_Data *data;
743
744 if (terminate)
745 break;
746
747 nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout);
748 if (nfds < 0)
749 {
750 ERR("An error occurred when reading the epoll fd.");
751 ERR("%s", strerror(errno));
752 break;
753 }
754 if (nfds == 0) // timeout occurred
755 {
756 timeout = -1;
757 if (!timeout_func)
758 ERR("Timeout expired, but no timeout function set.");
759 else
760 timeout_func();
761 }
762
763 for (n = 0; n < nfds; n++)
764 {
765 data = events[n].data.ptr;
766 Fd_Flags flags = 0;
767
768 if (!data)
769 continue;
770
771 if (data->deleted)
772 continue;
773
774 if (!data->callback)
775 continue;
776
777 if (events[n].events & EPOLLIN)
778 flags |= FD_READ;
779 if (events[n].events & EPOLLOUT)
780 flags |= FD_WRITE;
781 if (events[n].events & EPOLLERR)
782 flags |= FD_ERROR;
783 data->callback(data->fd, flags, (void *)data->user_data);
784 }
785
786 EINA_LIST_FREE(deleted_watch_list, data)
787 free(data);
788
789 _update_timeout();
790 }
791
792 running = EINA_FALSE;
793}
794
795ssize_t
796cserve2_client_read(Client *client, void *buf, size_t len)
797{
798 return recv(client->socket, buf, len, MSG_DONTWAIT);
799}
800
801ssize_t
802cserve2_client_write(Client *client, const void *data, size_t size)
803{
804 return send(client->socket, data, size, MSG_NOSIGNAL | MSG_DONTWAIT);
805}