sync
[platform/core/appfw/heynoti.git] / heynoti.c
1 /*
2  * heynoti
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Jayoun Lee <airjany@samsung.com>, Sewook Park <sewook7.park@samsung.com>,
7  * Jaeho Lee <jaeho81.lee@samsung.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  */
22
23
24
25 #define _GNU_SOURCE
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/inotify.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <linux/version.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <signal.h>
36 #include <poll.h>
37 #include <glib.h>
38 #include <sys/utsname.h>
39
40 #include "heynoti-internal.h"
41
42 #define AU_PREFIX_SYSNOTI "SYS"
43
44 #ifndef NOTI_ROOT
45 #  define NOTI_ROOT "/opt/share/noti"
46 #endif
47
48
49 #define NODAC_PERMISSION
50
51 struct noti_slot {
52         int wd;
53         void *cb_data;
54         void (*cb) (void *);
55         uint32_t mask;
56 };
57 typedef struct noti_slot nslot;
58
59 static int __make_noti_root(const char *p);
60 static int __make_noti_file(const char *p);
61 static inline int __make_noti_path(char *path, int size, const char *name);
62 static int __read_proc(const char *path, char *buf, int size);
63 static void __clear_nslot_list(GList *g_ns);
64 static struct noti_cont *__get_noti_cont(int fd);
65 static int __handle_callback(struct noti_cont *nc, int wd, uint32_t mask);
66 static int __handle_event(int fd);
67 static inline int __get_wd(int fd, const char *notipath);
68 static int __add_wd(struct noti_cont *nc, int wd, uint32_t mask,
69                   const char *notipath);
70 static int __add_noti(int fd, const char *notipath, void (*cb) (void *),
71                     void *data, uint32_t mask);
72 static int _del_noti(struct noti_cont *nc, int wd, void (*cb) (void *),
73                      const char *notipath);
74 static int del_noti(int fd, const char *notipath, void (*cb) (void *));
75
76
77 int slot_comp(struct noti_slot *a, struct noti_slot *b)
78 {
79         int r;
80
81         r = a->wd - b->wd;
82         if (r != 0)
83                 return r;
84
85         r = (int)(a->cb - b->cb);
86         return r;
87 }
88
89 typedef enum {
90         H_NONE,
91         H_SIGNAL,
92         H_ECORE,
93         H_GLIB,
94 } htype;
95
96 struct noti_cont {
97         int fd;
98         GList *g_ns;
99
100         htype ht;
101
102         int signo;
103         struct sigaction oldact;
104
105         void *handler;
106 };
107 typedef struct noti_cont ncont;
108
109 static const char *noti_root = NOTI_ROOT;
110 static GList *g_nc;
111
112 static int __make_noti_root(const char *p)
113 {
114         int fd;
115
116         if ((fd = open(p, O_RDONLY)) == -1 )
117                 return -1;
118
119         close(fd);
120
121         return 0;
122 }
123
124 static int __make_noti_file(const char *p)
125 {
126         int fd;
127
128         if ((fd = open(p, O_RDONLY)) == -1)
129                 return -1;
130
131         close(fd);
132
133         return 0;
134 }
135
136 static inline int __make_noti_path(char *path, int size, const char *name)
137 {
138         return snprintf(path, size, "%s/%s", noti_root, name);
139 }
140
141 static int __read_proc(const char *path, char *buf, int size)
142 {
143         int fd;
144         int ret;
145
146         if (buf == NULL || path == NULL) {
147                 UTIL_DBG("%s: path or buf is NULL\n", __func__);
148                 errno = EINVAL;
149                 return -1;
150         }
151
152         fd = open(path, O_RDONLY);
153         util_retvm_if(fd == -1, -1, "File open error: %s", strerror(errno));
154
155         memset(buf, 0x0, size);
156         ret = read(fd, buf, size);
157         close(fd);
158
159         return ret;
160 }
161
162 #define PROC_VERSION "/proc/version"
163
164 static int __check_kern_ver()
165 {
166         struct utsname nm;
167         int ret;
168         char buf[1024];
169         int v1;
170         int v2;
171         int v3;
172
173         ret = uname(&nm);
174         if (ret == 0) {
175                 strncpy(buf, nm.release, sizeof(buf));
176
177                 ret = sscanf(buf, "%d.%d.%d", &v1, &v2, &v3);
178         } else {
179                 __read_proc(PROC_VERSION, buf, sizeof(buf));
180
181                 ret = sscanf(buf, "Linux version %d.%d.%d", &v1, &v2, &v3);
182         }
183
184         if (ret == 3) {
185                 if(KERNEL_VERSION(v1, v2, v3) < KERNEL_VERSION(2, 6, 13)) {
186                         return -1;
187                 } else {
188                         return 0;
189                 }
190         }
191
192         return -1;
193 }
194
195 static void __clear_nslot_list(GList *g_ns)
196 {
197         struct noti_slot *t;
198         GList *it = NULL;
199
200         for (it = g_ns; it != NULL; it = g_list_next(it)) {
201                 t = (struct noti_slot *)it->data;
202                 free(t);
203         }
204
205 }
206
207 static struct noti_cont *__get_noti_cont(int fd)
208 {
209         struct noti_cont *r = NULL;
210         GList *it;
211
212         for (it = g_nc; it != NULL; it = g_list_next(it)) {
213                 if (it->data) {
214                         r = (struct noti_cont *)it->data;
215                         if (r->fd == fd) {
216                                 break;
217                         } else {
218                                 r = NULL;
219                         }
220                 }
221         }
222
223         return r;
224 }
225
226 static int __handle_callback(struct noti_cont *nc, int wd, uint32_t mask)
227 {
228         struct noti_slot *t;
229         GList *it = NULL;
230
231         if (!nc->g_ns)
232                 return 0;
233
234         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
235                 t = (struct noti_slot *)it->data;
236                 if (t->wd == wd) {
237                         if ((mask & t->mask) && t->cb) {
238                                 t->cb(t->cb_data);
239                         }
240                 }
241         }
242
243         return 0;
244 }
245
246 static int __handle_event(int fd)
247 {
248         int r;
249         struct inotify_event ie;
250         char name[FILENAME_MAX] = {0, };
251
252         struct noti_cont *nc;
253
254         nc = __get_noti_cont(fd);
255         util_warn_if(nc == NULL, "Non-registered file descriptor");
256
257         r = read(fd, &ie, sizeof(ie));
258
259         while (r > 0) {
260                 if (nc)
261                         __handle_callback(nc, ie.wd, ie.mask);
262
263                 if(ie.len > SSIZE_MAX)
264                         return -1;
265
266                 if (ie.len > 0u) {
267                         read(fd, name, (ie.len > FILENAME_MAX) ? (size_t)FILENAME_MAX : (size_t) ie.len);
268                 }
269
270                 r = read(fd, &ie, sizeof(ie));
271         }
272
273         return 0;
274 }
275
276 API int heynoti_poll_event(int fd)
277 {
278         int r;
279         struct noti_cont *nc;
280         struct pollfd fds[1];
281
282         nc = __get_noti_cont(fd);
283         if (nc == NULL) {
284                 UTIL_ERR("Non-registered file descriptor : %d", fd);
285                 errno = EBADF;
286                 return -1;
287         }
288
289         if (nc->ht != H_NONE) {
290                 UTIL_ERR("Another handler already in progress");
291                 errno = EALREADY;
292                 return -1;
293         }
294
295         fds[0].fd = nc->fd;
296         fds[0].events = POLLIN;
297
298         r = poll(fds, 1, -1);
299         util_retvm_if(r == -1, -1, "Error: poll : %s", strerror(errno));
300
301         if (fds[0].revents & POLLIN)
302                 __handle_event(fd);
303
304         return r;
305 }
306
307 static inline int __get_wd(int fd, const char *notipath)
308 {
309         return inotify_add_watch(fd, notipath, IN_ACCESS);
310 }
311
312 static int __add_wd(struct noti_cont *nc, int wd, uint32_t mask,
313                     const char *notipath)
314 {
315         int r;
316         uint32_t mask_all;
317         struct noti_slot *t;
318         GList *it;
319
320         mask_all = 0;
321         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
322                 t = (struct noti_slot *)it->data;
323                 if (t->wd == wd) {
324                         mask_all |= t->mask;
325                 }
326         }
327
328         mask_all |= mask;
329
330         r = inotify_add_watch(nc->fd, notipath, mask_all);
331         return r;
332 }
333
334 static int __add_noti(int fd, const char *notipath, void (*cb) (void *),
335                       void *data, uint32_t mask)
336 {
337         int r;
338         int wd;
339         struct noti_cont *nc;
340         struct noti_slot t, *n;
341         struct noti_slot *f = NULL;
342         GList *it;
343
344         nc = __get_noti_cont(fd);
345         if (nc == NULL) {
346                 UTIL_DBG("Bad file descriptor");
347                 errno = EBADF;
348                 return -1;
349         }
350
351         wd = __get_wd(fd, notipath);
352         util_retvm_if(wd == -1, -1, "Error: add noti: %s", strerror(errno));
353
354         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
355                 if (it->data) {
356                         f = (struct noti_slot *)it->data;
357                         if (f->wd == wd && f->cb == cb) {
358                                 break;
359                         } else {
360                                 f = NULL;
361                         }
362                 }
363         }
364
365         if (f) {
366                 __add_wd(nc, wd, 0, notipath);
367                 errno = EALREADY;
368                 return -1;
369         }
370
371         r = __add_wd(nc, wd, mask, notipath);
372         util_retvm_if(r == -1, -1, "Error: add noti: %s", strerror(errno));
373
374         n = calloc(1, sizeof(nslot));
375         util_retvm_if(n == NULL, -1, "Error: add noti: %s", strerror(errno));
376
377         n->wd = wd;
378         n->cb_data = data;
379         n->cb = cb;
380         n->mask = mask;
381         nc->g_ns = g_list_append(nc->g_ns, (gpointer) n);
382
383         return 0;
384 }
385
386 API int heynoti_subscribe(int fd, const char *noti, void (*cb) (void *),
387                           void *data)
388 {
389         char notipath[FILENAME_MAX];
390
391         if (noti == NULL || cb == NULL) {
392                 UTIL_DBG("Error: add noti: Invalid input");
393                 errno = EINVAL;
394                 return -1;
395         }
396
397         __make_noti_path(notipath, sizeof(notipath), noti);
398         UTIL_DBG("add watch: [%s]", notipath);
399
400         return __add_noti(fd, notipath, cb, data, IN_CLOSE_WRITE | IN_DELETE);
401 }
402
403 static int _del_noti(struct noti_cont *nc, int wd, void (*cb) (void *),
404                      const char *notipath)
405 {
406         int r = 0;
407         struct noti_slot *t;
408         int n_del;
409         int n_remain;
410         GList *it;
411         GList *it_next;
412
413         n_del = 0;
414         n_remain = 0;
415
416         it = nc->g_ns;
417         while (it) {
418                 it_next = it->next;
419
420                 if (it->data) {
421                         t = (struct noti_slot *)it->data;
422                         if (t->wd == wd) {
423                                 if (cb == NULL || cb == t->cb) {
424                                         nc->g_ns = g_list_remove(nc->g_ns, t);
425                                         free(t);
426                                         n_del++;
427                                 } else {
428                                         n_remain++;
429                                 }
430                         }
431                 }
432
433                 it = it_next;
434         }
435
436         if (n_remain == 0)
437                 return inotify_rm_watch(nc->fd, wd);
438
439         r = __add_wd(nc, wd, 0, notipath);
440
441         if (n_del == 0) {
442                 UTIL_DBG("Error: nothing deleted");
443                 errno = ENOENT;
444                 return -1;
445         }
446
447         return r;
448 }
449
450 static int del_noti(int fd, const char *notipath, void (*cb) (void *))
451 {
452         int wd;
453         struct noti_cont *nc;
454
455         nc = __get_noti_cont(fd);
456         if (nc == NULL) {
457                 UTIL_DBG("Bad file descriptor");
458                 errno = EBADF;
459                 return -1;
460         }
461
462         /* get wd */
463         wd = __get_wd(fd, notipath);
464         util_retv_if(wd == -1, -1);
465
466         return _del_noti(nc, wd, cb, notipath);
467 }
468
469 API int heynoti_unsubscribe(int fd, const char *noti, void (*cb) (void *))
470 {
471         int r;
472         char notipath[FILENAME_MAX];
473
474         if (fd < 0 || noti == NULL) {
475                 errno = EINVAL;
476                 return -1;
477         }
478
479         __make_noti_path(notipath, sizeof(notipath), noti);
480         UTIL_DBG("del watch: [%s]", notipath);
481
482         r = del_noti(fd, notipath, cb);
483         util_warn_if(r == -1, "Error: del [%s]: %s", noti, strerror(errno));
484
485         return r;
486 }
487
488 API int heynoti_publish(const char *noti)
489 {
490         int fd;
491         char *path;
492         char notipath[FILENAME_MAX];
493         struct stat sb;
494
495         if (noti == NULL) {
496                 UTIL_DBG("Error: send noti: Invalid input");
497                 errno = EINVAL;
498                 return -1;
499         }
500
501         path = strstr(noti, "/");
502         if (path) {
503                 snprintf(notipath, sizeof(notipath), "%s", noti);
504         } else {
505                 __make_noti_path(notipath, sizeof(notipath), noti);
506         }
507         UTIL_DBG("send noti: [%s]", notipath);
508
509         fd = open(notipath, O_TRUNC | O_WRONLY);
510
511         util_retvm_if(fd == -1, -1, "Error: send noti: %s", strerror(errno));
512
513         /*
514         fstat(fd, &sb);
515         if(sb.st_uid != getuid())
516                 fchown(fd, getuid(), getgid());
517         if(sb.st_mode & 0033)
518                 fchmod(fd, 0644);
519         */
520
521         close(fd);
522         return 0;
523 }
524
525 API int heynoti_init()
526 {
527         int r;
528         int fd;
529
530         struct noti_cont *nc;
531
532         if(__check_kern_ver() < 0) {
533                 UTIL_ERR("inotify requires kernel version >= 2.6.13 ");
534                 errno = EPERM;
535                 return -1;
536         }
537
538         fd = inotify_init();
539         util_retvm_if(fd == -1, -1, "inotify init: %s", strerror(errno));
540
541         r = fcntl(fd, F_SETFD, FD_CLOEXEC);
542         util_retvm_if(r < 0, -1, "fcntl error : %s", strerror(errno));
543         r = fcntl(fd, F_SETFL, O_NONBLOCK);
544         util_retvm_if(r < 0, -1, "fcntl error : %s", strerror(errno));
545
546         r = __make_noti_root(noti_root);
547         if (r == -1) {
548                 UTIL_ERR("make noti root: %s : %s", noti_root, strerror(errno));
549                 close(fd);
550                 return -1;
551         }
552
553         nc = calloc(1, sizeof(struct noti_cont));
554         if (nc == NULL) {
555                 close(fd);
556                 return -1;
557         }
558
559         nc->fd = fd;
560         /*sglib_ncont_add(&nc_h, nc); */
561         g_nc = g_list_append(g_nc, (gpointer) nc);
562
563         return fd;
564 }
565
566 API int heynoti_get_pnoti_name(pid_t pid, const char *name, char *buf,
567                                int buf_size)
568 {
569         int ret;
570
571         if (!name)
572                 return -1;
573
574         ret = snprintf(buf, buf_size, ".%d_%s", pid, name);
575
576         if (ret >= buf_size)
577                 return -1;
578         else
579                 return 0;
580 }
581
582 API int heynoti_get_snoti_name(const char *name, char *buf, int buf_size)
583 {
584         int ret;
585
586         if (!name)
587                 return -1;
588
589         ret = snprintf(buf, buf_size, ".%s_%s", AU_PREFIX_SYSNOTI, name);
590
591         if (ret >= buf_size)
592                 return -1;
593         else
594                 return 0;
595 }
596
597 gboolean gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
598 {
599         int fd;
600
601         /* need condition check?? */
602
603         fd = g_io_channel_unix_get_fd(src);
604         __handle_event(fd);
605
606         return TRUE;
607 }
608
609 API int heynoti_attach_handler(int fd)
610 {
611         int ret;
612         struct noti_cont *nc = NULL;
613         GSource *src;
614         GIOChannel *gio;
615
616         nc = __get_noti_cont(fd);
617         if (nc == NULL) {
618                 UTIL_ERR("Non-registered file descriptor : %d", fd);
619                 errno = EBADF;
620                 return -1;
621         }
622
623         switch (nc->ht) {
624         case H_NONE:
625                 /* do nothing */
626                 break;
627         case H_GLIB:
628                 UTIL_ERR("glib based handler now in progress");
629                 errno = EINPROGRESS;
630                 break;
631         default:
632                 UTIL_ERR("Another handler already in progress: %d", nc->ht);
633                 errno = EALREADY;
634                 break;
635         }
636         util_retv_if(nc->ht != H_NONE, -1);
637
638         gio = g_io_channel_unix_new(fd);
639         util_retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
640
641         g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
642
643         src = g_io_create_watch(gio, G_IO_IN);
644         g_source_set_callback(src, (GSourceFunc) gio_cb, NULL, NULL);
645         ret = g_source_attach(src, NULL);
646         g_io_channel_unref(gio);
647         g_source_unref(src);
648         if (!ret)
649                 return -1;
650
651         nc->handler = src;
652         nc->ht = H_GLIB;
653
654         return 0;
655 }
656
657 API int heynoti_detach_handler(int fd)
658 {
659         struct noti_cont *nc = NULL;
660
661         nc = __get_noti_cont(fd);
662         if (nc == NULL) {
663                 UTIL_ERR("Non-registered file descriptor : %d", fd);
664                 errno = EBADF;
665                 return -1;
666         }
667
668         switch (nc->ht) {
669         case H_GLIB:
670                 g_source_destroy(nc->handler);
671                 nc->handler = NULL;
672                 nc->ht = H_NONE;
673                 break;
674         default:
675                 /* do nothing ?? */
676                 break;
677         }
678
679         return 0;
680 }
681
682 API void heynoti_close(int fd)
683 {
684         struct noti_cont *r = NULL;
685
686         r = __get_noti_cont(fd);
687         util_warn_if(r == NULL, "Non-registered file descriptor");
688
689         if (r) {
690                 if (r->ht == H_GLIB)
691                         heynoti_detach_handler(fd);
692
693                 __clear_nslot_list(r->g_ns);
694                 g_list_free(r->g_ns);
695                 close(r->fd);
696
697                 g_nc = g_list_remove(g_nc, (gconstpointer) r);
698                 free(r);
699         }
700 }
701