Fix the issue that changed callback is not working
[platform/core/appfw/notification-service.git] / service_common.c
1 /*
2  * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <pthread.h>
20 #include <secure_socket.h>
21 #include <packet.h>
22 #include <errno.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/timerfd.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <Eina.h>
29
30 #include "service_common.h"
31
32 #define EVT_CH          'e'
33 #define EVT_END_CH      'x'
34
35 int errno;
36
37 struct service_event_item {
38         enum {
39                 SERVICE_EVENT_TIMER,
40         } type;
41
42         union {
43                 struct {
44                         int fd;
45                 } timer;
46         } info;
47
48         int (*event_cb)(struct service_context *svc_cx, void *data);
49         void *cbdata;
50 };
51
52 /*!
53  * \note
54  * Server information and global (only in this file-scope) variables are defined
55  */
56 struct service_context {
57         pthread_t server_thid; /*!< Server thread Id */
58         int fd; /*!< Server socket handle */
59
60         Eina_List *tcb_list; /*!< TCB list, list of every thread for client connections */
61
62         Eina_List *packet_list;
63         pthread_mutex_t packet_list_lock;
64         int evt_pipe[PIPE_MAX];
65         int tcb_pipe[PIPE_MAX];
66
67         int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data);
68         void *service_thread_data;
69
70         Eina_List *event_list;
71 };
72
73 struct packet_info {
74         struct tcb *tcb;
75         struct packet *packet;
76 };
77
78 /*!
79  * \note
80  * Thread Control Block
81  * - The main server will create a thread for every client connections.
82  *   When a new client is comming to us, this TCB block will be allocated and initialized.
83  */
84 struct tcb { /* Thread controll block */
85         struct service_context *svc_ctx;
86         pthread_t thid; /*!< Thread Id */
87         int fd; /*!< Connection handle */
88         enum tcb_type type;
89 };
90
91 /*!
92  * Do services for clients
93  * Routing packets to destination processes.
94  * CLIENT THREAD
95  */
96 static void *client_packet_pump_main(void *data)
97 {
98         struct tcb *tcb = data;
99         struct service_context *svc_ctx = tcb->svc_ctx;
100         struct packet *packet;
101         fd_set set;
102         char *ptr;
103         int size;
104         int packet_offset;
105         int recv_offset;
106         int pid;
107         long ret;
108         char evt_ch = EVT_CH;
109         enum {
110                 RECV_INIT,
111                 RECV_HEADER,
112                 RECV_PAYLOAD,
113                 RECV_DONE,
114         } recv_state;
115         struct packet_info *packet_info;
116         Eina_List *l;
117
118         ret = 0;
119         recv_state = RECV_INIT;
120         /*!
121          * \note
122          * To escape from the switch statement, we use this ret value
123          */
124         while (ret == 0) {
125                 FD_ZERO(&set);
126                 FD_SET(tcb->fd, &set);
127                 ret = select(tcb->fd + 1, &set, NULL, NULL, NULL);
128                 if (ret < 0) {
129                         ret = -errno;
130                         if (errno == EINTR) {
131                                 ret = 0;
132                                 continue;
133                         }
134                         free(ptr);
135                         ptr = NULL;
136                         break;
137                 } else if (ret == 0) {
138                         ret = -ETIMEDOUT;
139                         free(ptr);
140                         ptr = NULL;
141                         break;
142                 }
143
144                 if (!FD_ISSET(tcb->fd, &set)) {
145                         ret = -EINVAL;
146                         free(ptr);
147                         ptr = NULL;
148                         break;
149                 }
150                 
151                 /*!
152                  * \TODO
153                  * Service!!! Receive packet & route packet
154                  */
155                 switch (recv_state) {
156                 case RECV_INIT:
157                         size = packet_header_size();
158                         packet_offset = 0;
159                         recv_offset = 0;
160                         packet = NULL;
161                         ptr = malloc(size);
162                         if (!ptr) {
163                                 ret = -ENOMEM;
164                                 break;
165                         }
166                         recv_state = RECV_HEADER;
167                         /* Go through, don't break from here */
168                 case RECV_HEADER:
169                         ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
170                         if (ret <= 0) {
171                                 if (ret == 0)
172                                         ret = -ECANCELED;
173                                 free(ptr);
174                                 ptr = NULL;
175                                 break;
176                         }
177
178                         recv_offset += ret;
179                         ret = 0;
180
181                         if (recv_offset == size) {
182                                 packet = packet_build(packet, packet_offset, ptr, size);
183                                 free(ptr);
184                                 ptr = NULL;
185                                 if (!packet) {
186                                         ret = -EFAULT;
187                                         break;
188                                 }
189
190                                 packet_offset += recv_offset;
191
192                                 size = packet_payload_size(packet);
193                                 if (size <= 0) {
194                                         recv_state = RECV_DONE;
195                                         recv_offset = 0;
196                                         break;
197                                 }
198
199                                 recv_state = RECV_PAYLOAD;
200                                 recv_offset = 0;
201
202                                 ptr = malloc(size);
203                                 if (!ptr) {
204                                         ret = -ENOMEM;
205                                 }
206                         }
207                         break;
208                 case RECV_PAYLOAD:
209                         ret = secure_socket_recv(tcb->fd, ptr, size - recv_offset, &pid);
210                         if (ret <= 0) {
211                                 if (ret == 0)
212                                         ret = -ECANCELED;
213                                 free(ptr);
214                                 ptr = NULL;
215                                 break;
216                         }
217
218                         recv_offset += ret;
219                         ret = 0;
220
221                         if (recv_offset == size) {
222                                 packet = packet_build(packet, packet_offset, ptr, size);
223                                 free(ptr);
224                                 ptr = NULL;
225                                 if (!packet) {
226                                         ret = -EFAULT;
227                                         break;
228                                 }
229
230                                 packet_offset += recv_offset;
231
232                                 recv_state = RECV_DONE;
233                                 recv_offset = 0;
234                         }
235                         break;
236                 case RECV_DONE:
237                 default:
238                         /* Dead code */
239                         break;
240                 }
241
242                 if (recv_state == RECV_DONE) {
243                         /*!
244                          * Push this packet to the packet list with TCB
245                          * Then the service main function will get this.
246                          */
247                         packet_info = malloc(sizeof(*packet_info));
248                         if (!packet_info) {
249                                 ret = -errno;
250                                 packet_destroy(packet);
251                                 break;
252                         }
253
254                         packet_info->packet = packet;
255                         packet_info->tcb = tcb;
256
257                         CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
258                         svc_ctx->packet_list = eina_list_append(svc_ctx->packet_list, packet_info);
259                         CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
260
261                         if (write(svc_ctx->evt_pipe[PIPE_WRITE], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
262                                 ret = -errno;
263                                 CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
264                                 svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info);
265                                 CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
266
267                                 packet_destroy(packet);
268                                 free(packet_info);
269                                 break;
270                         } else {
271                                 recv_state = RECV_INIT;
272                         }
273                 }
274         }
275
276         CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
277         EINA_LIST_FOREACH(svc_ctx->packet_list, l, packet_info) {
278                 if (packet_info->tcb == tcb) {
279                         packet_info->tcb = NULL;
280                 }
281         }
282         CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
283
284         /*!
285          * \note
286          * Emit a signal to collect this TCB from the SERVER THREAD.
287          */
288         write(svc_ctx->tcb_pipe[PIPE_WRITE], &tcb, sizeof(tcb)) != sizeof(tcb);
289
290         return (void *)ret;
291 }
292
293 /*!
294  * \note
295  * SERVER THREAD
296  */
297 static inline struct tcb *tcb_create(struct service_context *svc_ctx, int fd)
298 {
299         struct tcb *tcb;
300         int status;
301
302         tcb = malloc(sizeof(*tcb));
303         if (!tcb) {
304                 return NULL;
305         }
306
307         tcb->fd = fd;
308         tcb->svc_ctx = svc_ctx;
309         tcb->type = TCB_CLIENT_TYPE_APP;
310
311         status = pthread_create(&tcb->thid, NULL, client_packet_pump_main, tcb);
312         if (status != 0) {
313                 free(tcb);
314                 return NULL;
315         }
316
317         svc_ctx->tcb_list = eina_list_append(svc_ctx->tcb_list, tcb);
318         return tcb;
319 }
320
321 /*!
322  * \note
323  * SERVER THREAD
324  */
325 static inline void tcb_teminate_all(struct service_context *svc_ctx)
326 {
327         struct tcb *tcb;
328         void *ret;
329         int status;
330
331         /*!
332          * We don't need to make critical section on here.
333          * If we call this after terminate the server thread first.
334          * Then there is no other thread to access tcb_list.
335          */
336         EINA_LIST_FREE(svc_ctx->tcb_list, tcb) {
337                 /*!
338                  * ASSERT(tcb->fd >= 0);
339                  */
340                 secure_socket_destroy_handle(tcb->fd);
341
342                 status = pthread_join(tcb->thid, &ret);
343
344                 free(tcb);
345         }
346 }
347
348 /*!
349  * \note
350  * SERVER THREAD
351  */
352 static inline void tcb_destroy(struct service_context *svc_ctx, struct tcb *tcb)
353 {
354         void *ret;
355         int status;
356
357         svc_ctx->tcb_list = eina_list_remove(svc_ctx->tcb_list, tcb);
358         /*!
359          * ASSERT(tcb->fd >= 0);
360          * Close the connection, and then collecting the return value of thread
361          */
362         secure_socket_destroy_handle(tcb->fd);
363
364         status = pthread_join(tcb->thid, &ret);
365
366         free(tcb);
367 }
368
369 /*!
370  * \note
371  * SERVER THREAD
372  */
373 static inline int find_max_fd(struct service_context *svc_ctx)
374 {
375         int fd;
376         Eina_List *l;
377         struct service_event_item *item;
378
379         fd = svc_ctx->fd > svc_ctx->tcb_pipe[PIPE_READ] ? svc_ctx->fd : svc_ctx->tcb_pipe[PIPE_READ];
380         fd = fd > svc_ctx->evt_pipe[PIPE_READ] ? fd : svc_ctx->evt_pipe[PIPE_READ];
381
382         EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
383                 if (item->type == SERVICE_EVENT_TIMER && fd < item->info.timer.fd)
384                         fd = item->info.timer.fd;
385         }
386
387         fd += 1;
388         return fd;
389 }
390
391 /*!
392  * \note
393  * SERVER THREAD
394  */
395 static inline void update_fdset(struct service_context *svc_ctx, fd_set *set)
396 {
397         Eina_List *l;
398         struct service_event_item *item;
399
400         FD_ZERO(set);
401         FD_SET(svc_ctx->fd, set);
402         FD_SET(svc_ctx->tcb_pipe[PIPE_READ], set);
403         FD_SET(svc_ctx->evt_pipe[PIPE_READ], set);
404
405         EINA_LIST_FOREACH(svc_ctx->event_list, l, item) {
406                 if (item->type == SERVICE_EVENT_TIMER)
407                         FD_SET(item->info.timer.fd, set);
408         }
409 }
410
411 /*!
412  * \note
413  * SERVER THREAD
414  */
415 static inline void processing_timer_event(struct service_context *svc_ctx, fd_set *set)
416 {
417         uint64_t expired_count;
418         Eina_List *l;
419         Eina_List *n;
420         struct service_event_item *item;
421
422         EINA_LIST_FOREACH_SAFE(svc_ctx->event_list, l, n, item) {
423                 switch (item->type) {
424                 case SERVICE_EVENT_TIMER:
425                         if (!FD_ISSET(item->info.timer.fd, set))
426                                 break;
427
428                         if (read(item->info.timer.fd, &expired_count, sizeof(expired_count)) == sizeof(expired_count)) {
429                                 if (item->event_cb(svc_ctx, item->cbdata) >= 0)
430                                         break;
431                         }
432
433                         if (!eina_list_data_find(svc_ctx->event_list, item))
434                                 break;
435
436                         svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
437                         close(item->info.timer.fd);
438
439                         free(item);
440                         break;
441                 default:
442                         break;
443                 }
444         }
445 }
446
447 /*!
448  * Accept new client connections
449  * And create a new thread for service.
450  *
451  * Create Client threads & Destroying them
452  * SERVER THREAD
453  */
454 static void *server_main(void *data)
455 {
456         struct service_context *svc_ctx = data;
457         fd_set set;
458         long ret;
459         int client_fd;
460         struct tcb *tcb;
461         int fd;
462         char evt_ch;
463         struct packet_info *packet_info;
464
465         while (1) {
466                 fd = find_max_fd(svc_ctx);
467                 update_fdset(svc_ctx, &set);
468
469                 ret = select(fd, &set, NULL, NULL, NULL);
470                 if (ret < 0) {
471                         ret = -errno;
472                         if (errno == EINTR) {
473                                 continue;
474                         }
475                         break;
476                 } else if (ret == 0) {
477                         ret = -ETIMEDOUT;
478                         break;
479                 }
480
481                 if (FD_ISSET(svc_ctx->fd, &set)) {
482                         client_fd = secure_socket_get_connection_handle(svc_ctx->fd);
483                         if (client_fd < 0) {
484                                 ret = -EFAULT;
485                                 break;
486                         }
487
488                         tcb = tcb_create(svc_ctx, client_fd);
489                         if (!tcb)
490                                 secure_socket_destroy_handle(client_fd);
491                 } 
492
493                 if (FD_ISSET(svc_ctx->tcb_pipe[PIPE_READ], &set)) {
494                         if (read(svc_ctx->tcb_pipe[PIPE_READ], &tcb, sizeof(tcb)) != sizeof(tcb)) {
495                                 ret = -EFAULT;
496                                 break;
497                         }
498
499                         /*!
500                          * \note
501                          * Invoke the service thread main, to notify the termination of a TCB
502                          */
503                         ret = svc_ctx->service_thread_main(tcb, NULL, svc_ctx->service_thread_data);
504
505                         /*!
506                          * at this time, the client thread can access this tcb.
507                          * how can I protect this TCB from deletion without disturbing the server thread?
508                          */
509                         tcb_destroy(svc_ctx, tcb);
510                 } 
511
512                 if (FD_ISSET(svc_ctx->evt_pipe[PIPE_READ], &set)) {
513                         if (read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch)) != sizeof(evt_ch)) {
514                                 ret = -EFAULT;
515                                 break;
516                         }
517
518                         CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
519                         packet_info = eina_list_nth(svc_ctx->packet_list, 0);
520                         svc_ctx->packet_list = eina_list_remove(svc_ctx->packet_list, packet_info);
521                         CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
522
523                         /*!
524                          * \CRITICAL
525                          * What happens if the client thread is terminated, so the packet_info->tcb is deleted
526                          * while processing svc_ctx->service_thread_main?
527                          */
528                         ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
529
530                         packet_destroy(packet_info->packet);
531                         free(packet_info);
532                 }
533
534                 processing_timer_event(svc_ctx, &set);
535                 /* If there is no such triggered FD? */
536         }
537
538         /*!
539          * Consuming all pended packets before terminates server thread.
540          *
541          * If the server thread is terminated, we should flush all pended packets.
542          * And we should services them.
543          * While processing this routine, the mutex is locked.
544          * So every other client thread will be slowed down, sequently, every clients can meet problems.
545          * But in case of termination of server thread, there could be systemetic problem.
546          * This only should be happenes while terminating the master daemon process.
547          */
548         CRITICAL_SECTION_BEGIN(&svc_ctx->packet_list_lock);
549         EINA_LIST_FREE(svc_ctx->packet_list, packet_info) {
550                 ret = read(svc_ctx->evt_pipe[PIPE_READ], &evt_ch, sizeof(evt_ch));
551                 ret = svc_ctx->service_thread_main(packet_info->tcb, packet_info->packet, svc_ctx->service_thread_data);
552                 packet_destroy(packet_info->packet);
553                 free(packet_info);
554         }
555         CRITICAL_SECTION_END(&svc_ctx->packet_list_lock);
556
557         tcb_teminate_all(svc_ctx);
558         return (void *)ret;
559 }
560
561 /*!
562  * \NOTE
563  * MAIN THREAD
564  */
565 struct service_context *service_common_create(const char *addr, int (*service_thread_main)(struct tcb *tcb, struct packet *packet, void *data), void *data)
566 {
567         int status;
568         struct service_context *svc_ctx;
569
570         if (!service_thread_main || !addr) {
571                 return NULL;
572         }
573
574         svc_ctx = calloc(1, sizeof(*svc_ctx));
575         if (!svc_ctx) {
576                 return NULL;
577         }
578
579         svc_ctx->fd = secure_socket_create_server(addr);
580         if (svc_ctx->fd < 0) {
581                 free(svc_ctx);
582                 return NULL;
583         }
584
585         svc_ctx->service_thread_main = service_thread_main;
586         svc_ctx->service_thread_data = data;
587
588         fcntl(svc_ctx->fd, F_SETFD, FD_CLOEXEC);
589
590         fcntl(svc_ctx->fd, F_SETFL, O_NONBLOCK);
591
592         if (pipe2(svc_ctx->evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
593                 secure_socket_destroy_handle(svc_ctx->fd);
594                 free(svc_ctx);
595                 return NULL;
596         }
597
598         if (pipe2(svc_ctx->tcb_pipe, O_NONBLOCK | O_CLOEXEC) < 0) {
599                 CLOSE_PIPE(svc_ctx->evt_pipe);
600                 secure_socket_destroy_handle(svc_ctx->fd);
601                 free(svc_ctx);
602                 return NULL;
603         }
604
605         status = pthread_mutex_init(&svc_ctx->packet_list_lock, NULL);
606         if (status != 0) {
607                 CLOSE_PIPE(svc_ctx->evt_pipe);
608                 CLOSE_PIPE(svc_ctx->tcb_pipe);
609                 secure_socket_destroy_handle(svc_ctx->fd);
610                 free(svc_ctx);
611                 return NULL;
612         }
613
614         status = pthread_create(&svc_ctx->server_thid, NULL, server_main, svc_ctx);
615         if (status != 0) {
616                 status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
617                 CLOSE_PIPE(svc_ctx->evt_pipe);
618                 CLOSE_PIPE(svc_ctx->tcb_pipe);
619                 secure_socket_destroy_handle(svc_ctx->fd);
620                 free(svc_ctx);
621                 return NULL;
622         }
623
624         return svc_ctx;
625 }
626
627 /*!
628  * \note
629  * MAIN THREAD
630  */
631 int service_common_destroy(struct service_context *svc_ctx)
632 {
633         int status;
634         void *ret;
635
636         if (!svc_ctx)
637                 return -EINVAL;
638
639         /*!
640          * \note
641          * Terminate server thread
642          */
643         secure_socket_destroy_handle(svc_ctx->fd);
644
645         status = pthread_join(svc_ctx->server_thid, &ret);
646
647         status = pthread_mutex_destroy(&svc_ctx->packet_list_lock);
648
649         CLOSE_PIPE(svc_ctx->evt_pipe);
650         CLOSE_PIPE(svc_ctx->tcb_pipe);
651         free(svc_ctx);
652         return 0;
653 }
654
655 /*!
656  * \note
657  * SERVER THREAD
658  */
659 int tcb_fd(struct tcb *tcb)
660 {
661         if (!tcb)
662                 return -EINVAL;
663
664         return tcb->fd;
665 }
666
667 /*!
668  * \note
669  * SERVER THREAD
670  */
671 int tcb_client_type(struct tcb *tcb)
672 {
673         if (!tcb)
674                 return -EINVAL;
675
676         return tcb->type;
677 }
678
679 /*!
680  * \note
681  * SERVER THREAD
682  */
683 int tcb_client_type_set(struct tcb *tcb, enum tcb_type type)
684 {
685         if (!tcb)
686                 return -EINVAL;
687
688         tcb->type = type;
689         return 0;
690 }
691
692 /*!
693  * \note
694  * SERVER THREAD
695  */
696 struct service_context *tcb_svc_ctx(struct tcb *tcb)
697 {
698         if (!tcb)
699                 return NULL;
700
701         return tcb->svc_ctx;
702 }
703
704 /*!
705  * \note
706  * SERVER THREAD
707  */
708 int service_common_unicast_packet(struct tcb *tcb, struct packet *packet)
709 {
710         if (!tcb || !packet)
711                 return -EINVAL;
712
713         return secure_socket_send(tcb->fd, (void *)packet_data(packet), packet_size(packet));
714 }
715
716 /*!
717  * \note
718  * SERVER THREAD
719  */
720 int service_common_multicast_packet(struct tcb *tcb, struct packet *packet, int type)
721 {
722         Eina_List *l;
723         struct tcb *target;
724         struct service_context *svc_ctx;
725         int ret;
726
727         if (!tcb || !packet)
728                 return -EINVAL;
729
730         svc_ctx = tcb->svc_ctx;
731
732         EINA_LIST_FOREACH(svc_ctx->tcb_list, l, target) {
733                 if (target == tcb || target->type != type) {
734                         continue;
735                 }
736
737                 ret = secure_socket_send(target->fd, (void *)packet_data(packet), packet_size(packet));
738         }
739         return 0;
740 }
741
742 /*!
743  * \note
744  * SERVER THREAD
745  */
746 struct service_event_item *service_common_add_timer(struct service_context *svc_ctx, double timer, int (*timer_cb)(struct service_context *svc_cx, void *data), void *data)
747 {
748         struct service_event_item *item;
749         struct itimerspec spec;
750
751         item = calloc(1, sizeof(*item));
752         if (!item) {
753                 return NULL;
754         }
755
756         item->type = SERVICE_EVENT_TIMER;
757         item->info.timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
758         if (item->info.timer.fd < 0) {
759                 free(item);
760                 return NULL;
761         }
762
763         spec.it_interval.tv_sec = (time_t)timer;
764         spec.it_interval.tv_nsec = (timer - spec.it_interval.tv_sec) * 1000000000;
765         spec.it_value.tv_sec = 0;
766         spec.it_value.tv_nsec = 0;
767
768         if (timerfd_settime(item->info.timer.fd, 0, &spec, NULL) < 0) {
769                 close(item->info.timer.fd);
770                 free(item);
771                 return NULL;
772         }
773
774         item->event_cb = timer_cb;
775         item->cbdata = data;
776
777         svc_ctx->event_list = eina_list_append(svc_ctx->event_list, item);
778         return item;
779 }
780
781 /*!
782  * \note
783  * SERVER THREAD
784  */
785 int service_common_del_timer(struct service_context *svc_ctx, struct service_event_item *item)
786 {
787         if (!eina_list_data_find(svc_ctx->event_list, item)) {
788                 return -EINVAL;
789         }
790
791         svc_ctx->event_list = eina_list_remove(svc_ctx->event_list, item);
792
793         close(item->info.timer.fd);
794         free(item);
795         return 0;
796 }
797
798 /* End of a file */