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