tizen beta release
[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 "/tmp/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 int __get_kern_ver();
64 static void __clear_nslot_list(GList *g_ns);
65 static struct noti_cont *__get_noti_cont(int fd);
66 static int __handle_callback(struct noti_cont *nc, int wd, uint32_t mask);
67 static int __handle_event(int fd);
68 static inline int __get_wd(int fd, const char *notipath);
69 static int __add_wd(struct noti_cont *nc, int wd, uint32_t mask,
70                   const char *notipath);
71 static int __add_noti(int fd, const char *notipath, void (*cb) (void *),
72                     void *data, uint32_t mask);
73 static int _del_noti(struct noti_cont *nc, int wd, void (*cb) (void *),
74                      const char *notipath);
75 static int del_noti(int fd, const char *notipath, void (*cb) (void *));
76
77
78 int slot_comp(struct noti_slot *a, struct noti_slot *b)
79 {
80         int r;
81
82         r = a->wd - b->wd;
83         if (r != 0)
84                 return r;
85
86         r = (int)(a->cb - b->cb);
87         return r;
88 }
89
90 typedef enum {
91         H_NONE,
92         H_SIGNAL,
93         H_ECORE,
94         H_GLIB,
95 } htype;
96
97 struct noti_cont {
98         int fd;
99         GList *g_ns;
100
101         htype ht;
102
103         int signo;
104         struct sigaction oldact;
105
106         void *handler;
107 };
108 typedef struct noti_cont ncont;
109
110 static const char *noti_root = NOTI_ROOT;
111 static GList *g_nc;
112
113 static int __make_noti_root(const char *p)
114 {
115         int fd;
116
117         if ((fd = open(p, O_RDONLY)) == -1) {
118                 if (mkdir(p, 0777) == -1)
119                         return -1;
120         } else {
121                 fchmod(fd, 0777);
122                 close(fd);
123         }
124         return 0;
125 }
126
127 static int __make_noti_file(const char *p)
128 {
129         int fd;
130
131 #ifdef NODAC_PERMISSION
132         if ((fd = open(p, O_RDONLY)) == -1) {
133                 if ((fd = open(p, O_CREAT, 0666)) < 0) {
134                         return -1;
135                 }
136                 fchmod(fd, 0666);
137                 close(fd);
138         } else {
139                 fchmod(fd, 0666);
140                 close(fd);
141         }
142 #else
143         if ((fd = open(p, O_RDONLY)) == -1) {
144                 if ((fd = open(p, O_CREAT, 0644)) < 0) {
145                         return -1;
146                 }
147                 close(fd);
148         } else {
149                 fchmod(fd, 0644);
150                 close(fd);
151         }
152 #endif
153         return 0;
154 }
155
156 static inline int __make_noti_path(char *path, int size, const char *name)
157 {
158         return snprintf(path, size, "%s/%s", noti_root, name);
159 }
160
161 static int __read_proc(const char *path, char *buf, int size)
162 {
163         int fd;
164         int ret;
165
166         if (buf == NULL || path == NULL) {
167                 UTIL_DBG("%s: path or buf is NULL\n", __func__);
168                 errno = EINVAL;
169                 return -1;
170         }
171
172         fd = open(path, O_RDONLY);
173         util_retvm_if(fd == -1, -1, "File open error: %s", strerror(errno));
174
175         memset(buf, 0x0, size);
176         ret = read(fd, buf, size);
177         close(fd);
178
179         return ret;
180 }
181
182 static int kern_ver;            /* Kernel version */
183 #define PROC_VERSION "/proc/version"
184
185 static int __get_kern_ver()
186 {
187         struct utsname nm;
188         int ret;
189         char buf[1024];
190         int v1;
191         int v2;
192         int v3;
193
194         util_retv_if(kern_ver != 0, kern_ver);
195
196         ret = uname(&nm);
197         if (ret == 0) {
198                 strncpy(buf, nm.release, sizeof(buf));
199
200                 ret = sscanf(buf, "%d.%d.%d", &v1, &v2, &v3);
201         } else {
202                 __read_proc(PROC_VERSION, buf, sizeof(buf));
203
204                 ret = sscanf(buf, "Linux version %d.%d.%d", &v1, &v2, &v3);
205         }
206
207         if (ret == 3) {
208                 kern_ver = KERNEL_VERSION(v1, v2, v3);
209                 return kern_ver;
210         }
211
212         return -1;
213 }
214
215 static void __clear_nslot_list(GList *g_ns)
216 {
217         struct noti_slot *t;
218         GList *it = NULL;
219
220         for (it = g_ns; it != NULL; it = g_list_next(it)) {
221                 t = (struct noti_slot *)it->data;
222                 free(t);
223         }
224
225 }
226
227 static struct noti_cont *__get_noti_cont(int fd)
228 {
229         struct noti_cont *r = NULL;
230         GList *it;
231
232         for (it = g_nc; it != NULL; it = g_list_next(it)) {
233                 if (it->data) {
234                         r = (struct noti_cont *)it->data;
235                         if (r->fd == fd) {
236                                 break;
237                         } else {
238                                 r = NULL;
239                         }
240                 }
241         }
242
243         return r;
244 }
245
246 static int __handle_callback(struct noti_cont *nc, int wd, uint32_t mask)
247 {
248         struct noti_slot *t;
249         GList *it = NULL;
250
251         if (!nc->g_ns)
252                 return 0;
253
254         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
255                 t = (struct noti_slot *)it->data;
256                 if (t->wd == wd) {
257                         if ((mask & t->mask) && t->cb) {
258                                 t->cb(t->cb_data);
259                         }
260                 }
261         }
262
263         return 0;
264 }
265
266 static int __handle_event(int fd)
267 {
268         int r;
269         struct inotify_event ie;
270         char name[FILENAME_MAX];
271
272         struct noti_cont *nc;
273
274         nc = __get_noti_cont(fd);
275         util_warn_if(nc == NULL, "Non-registered file descriptor");
276
277         r = read(fd, &ie, sizeof(ie));
278
279         while (r > 0) {
280                 if (nc)
281                         __handle_callback(nc, ie.wd, ie.mask);
282
283                 if (ie.len > 0)
284                         read(fd, name, ie.len);
285
286                 r = read(fd, &ie, sizeof(ie));
287         }
288
289         return 0;
290 }
291
292 API int heynoti_poll_event(int fd)
293 {
294         int r;
295         struct noti_cont *nc;
296         struct pollfd fds[1];
297
298         nc = __get_noti_cont(fd);
299         if (nc == NULL) {
300                 UTIL_ERR("Non-registered file descriptor : %d", fd);
301                 errno = EBADF;
302                 return -1;
303         }
304
305         if (nc->ht != H_NONE) {
306                 UTIL_ERR("Another handler already in progress");
307                 errno = EALREADY;
308                 return -1;
309         }
310
311         fds[0].fd = nc->fd;
312         fds[0].events = POLLIN;
313
314         r = poll(fds, 1, -1);
315         util_retvm_if(r == -1, -1, "Error: poll : %s", strerror(errno));
316
317         if (fds[0].revents & POLLIN)
318                 __handle_event(fd);
319
320         return r;
321 }
322
323 static inline int __get_wd(int fd, const char *notipath)
324 {
325         return inotify_add_watch(fd, notipath, IN_ACCESS);
326 }
327
328 static int __add_wd(struct noti_cont *nc, int wd, uint32_t mask,
329                     const char *notipath)
330 {
331         int r;
332         uint32_t mask_all;
333         struct noti_slot *t;
334         GList *it;
335
336         mask_all = 0;
337         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
338                 t = (struct noti_slot *)it->data;
339                 if (t->wd == wd) {
340                         mask_all |= t->mask;
341                 }
342         }
343
344         mask_all |= mask;
345
346         r = inotify_add_watch(nc->fd, notipath, mask_all);
347         return r;
348 }
349
350 static int __add_noti(int fd, const char *notipath, void (*cb) (void *),
351                       void *data, uint32_t mask)
352 {
353         int r;
354         int wd;
355         struct noti_cont *nc;
356         struct noti_slot t, *n;
357         struct noti_slot *f = NULL;
358         GList *it;
359
360         nc = __get_noti_cont(fd);
361         if (nc == NULL) {
362                 UTIL_DBG("Bad file descriptor");
363                 errno = EBADF;
364                 return -1;
365         }
366
367         __make_noti_file(notipath);
368
369         wd = __get_wd(fd, notipath);
370         util_retvm_if(wd == -1, -1, "Error: add noti: %s", strerror(errno));
371
372         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
373                 if (it->data) {
374                         f = (struct noti_slot *)it->data;
375                         if (f->wd == wd && f->cb == cb) {
376                                 break;
377                         } else {
378                                 f = NULL;
379                         }
380                 }
381         }
382
383         if (f) {
384                 __add_wd(nc, wd, 0, notipath);
385                 errno = EALREADY;
386                 return -1;
387         }
388
389         r = __add_wd(nc, wd, mask, notipath);
390         util_retvm_if(r == -1, -1, "Error: add noti: %s", strerror(errno));
391
392         n = calloc(1, sizeof(nslot));
393         util_retvm_if(n == NULL, -1, "Error: add noti: %s", strerror(errno));
394
395         n->wd = wd;
396         n->cb_data = data;
397         n->cb = cb;
398         n->mask = mask;
399         nc->g_ns = g_list_append(nc->g_ns, (gpointer) n);
400
401         return 0;
402 }
403
404 API int heynoti_subscribe_file(int fd, const char *path, void (*cb) (void *),
405                                void *data, uint32_t mask)
406 {
407         if (path == NULL || cb == NULL) {
408                 UTIL_DBG("Error: add noti: Invalid input");
409                 errno = EINVAL;
410                 return -1;
411         }
412
413         return __add_noti(fd, path, cb, data, mask);
414 }
415
416 API int heynoti_subscribe(int fd, const char *noti, void (*cb) (void *),
417                           void *data)
418 {
419         char notipath[FILENAME_MAX];
420
421         if (noti == NULL || cb == NULL) {
422                 UTIL_DBG("Error: add noti: Invalid input");
423                 errno = EINVAL;
424                 return -1;
425         }
426
427         __make_noti_path(notipath, sizeof(notipath), noti);
428         UTIL_DBG("add watch: [%s]", notipath);
429
430         return __add_noti(fd, notipath, cb, data, IN_CLOSE_WRITE | IN_DELETE);
431 }
432
433 static int _del_noti(struct noti_cont *nc, int wd, void (*cb) (void *),
434                      const char *notipath)
435 {
436         int r = 0;
437         struct noti_slot *t;
438         int n_del;
439         int n_remain;
440         GList *it;
441         GList *it_next;
442
443         n_del = 0;
444         n_remain = 0;
445
446         it = nc->g_ns;
447         while (it) {
448                 it_next = it->next;
449
450                 if (it->data) {
451                         t = (struct noti_slot *)it->data;
452                         if (t->wd == wd) {
453                                 if (cb == NULL || cb == t->cb) {
454                                         nc->g_ns = g_list_remove(nc->g_ns, t);
455                                         free(t);
456                                         n_del++;
457                                 } else {
458                                         n_remain++;
459                                 }
460                         }
461                 }
462
463                 it = it_next;
464         }
465
466         if (n_remain == 0)
467                 return inotify_rm_watch(nc->fd, wd);
468
469         r = __add_wd(nc, wd, 0, notipath);
470
471         if (n_del == 0) {
472                 UTIL_DBG("Error: nothing deleted");
473                 errno = ENOENT;
474                 return -1;
475         }
476
477         return r;
478 }
479
480 static int del_noti(int fd, const char *notipath, void (*cb) (void *))
481 {
482         int wd;
483         struct noti_cont *nc;
484
485         nc = __get_noti_cont(fd);
486         if (nc == NULL) {
487                 UTIL_DBG("Bad file descriptor");
488                 errno = EBADF;
489                 return -1;
490         }
491
492         /* get wd */
493         wd = __get_wd(fd, notipath);
494         util_retv_if(wd == -1, -1);
495
496         return _del_noti(nc, wd, cb, notipath);
497 }
498
499 API int heynoti_unsubscribe(int fd, const char *noti, void (*cb) (void *))
500 {
501         int r;
502         char notipath[FILENAME_MAX];
503
504         if (fd < 0 || noti == NULL) {
505                 errno = EINVAL;
506                 return -1;
507         }
508
509         __make_noti_path(notipath, sizeof(notipath), noti);
510         UTIL_DBG("del watch: [%s]", notipath);
511
512         r = del_noti(fd, notipath, cb);
513         util_warn_if(r == -1, "Error: del [%s]: %s", noti, strerror(errno));
514
515         return r;
516 }
517
518 API int heynoti_unsubscribe_file(int fd, const char *path, void (*cb) (void *))
519 {
520         int r;
521
522         if (fd < 0 || path == NULL) {
523                 errno = EINVAL;
524                 return -1;
525         }
526
527         r = del_noti(fd, path, cb);
528         util_warn_if(r == -1, "Error: del [%s]: %s", path, strerror(errno));
529
530         return r;
531 }
532
533 API int heynoti_publish(const char *noti)
534 {
535         int fd;
536         char *path;
537         char notipath[FILENAME_MAX];
538         struct stat sb;
539
540         if (noti == NULL) {
541                 UTIL_DBG("Error: send noti: Invalid input");
542                 errno = EINVAL;
543                 return -1;
544         }
545
546         path = strstr(noti, "/");
547         if (path) {
548                 snprintf(notipath, sizeof(notipath), "%s", noti);
549         } else {
550                 __make_noti_path(notipath, sizeof(notipath), noti);
551         }
552         UTIL_DBG("send noti: [%s]", notipath);
553
554 #ifdef NODAC_PERMISSION
555         fd = open(notipath, O_CREAT | O_TRUNC | O_RDWR, 0666);
556 #else
557         fd = open(notipath, O_CREAT | O_TRUNC | O_RDWR, 0644);
558 #endif
559         util_retvm_if(fd == -1, -1, "Error: send noti: %s", strerror(errno));
560
561         /*
562         fstat(fd, &sb);
563         if(sb.st_uid != getuid())
564                 fchown(fd, getuid(), getgid());
565         if(sb.st_mode & 0033)
566                 fchmod(fd, 0644);
567         */
568 #ifdef NODAC_PERMISSION
569         fchmod(fd, 0666);
570 #endif
571
572         close(fd);
573         return 0;
574 }
575
576 API int heynoti_init()
577 {
578         int r;
579         int fd;
580
581         struct noti_cont *nc;
582
583         if (__get_kern_ver() < KERNEL_VERSION(2, 6, 13)) {
584                 UTIL_ERR("inotify requires kernel version >= 2.6.13 ");
585                 errno = EPERM;
586                 return -1;
587         }
588
589         fd = inotify_init();
590         util_retvm_if(fd == -1, -1, "inotify init: %s", strerror(errno));
591
592         fcntl(fd, F_SETFD, FD_CLOEXEC);
593         fcntl(fd, F_SETFL, O_NONBLOCK);
594
595         r = __make_noti_root(noti_root);
596         if (r == -1) {
597                 UTIL_ERR("make noti root: %s : %s", noti_root, strerror(errno));
598                 close(fd);
599                 return -1;
600         }
601
602         nc = calloc(1, sizeof(struct noti_cont));
603         if (nc == NULL) {
604                 close(fd);
605                 return -1;
606         }
607
608         nc->fd = fd;
609         /*sglib_ncont_add(&nc_h, nc); */
610         g_nc = g_list_append(g_nc, (gpointer) nc);
611
612         return fd;
613 }
614
615 API int heynoti_get_pnoti_name(pid_t pid, const char *name, char *buf,
616                                int buf_size)
617 {
618         int ret;
619
620         if (!name)
621                 return -1;
622
623         ret = snprintf(buf, buf_size, ".%d_%s", pid, name);
624
625         if (ret >= buf_size)
626                 return -1;
627         else
628                 return 0;
629 }
630
631 API int heynoti_get_snoti_name(const char *name, char *buf, int buf_size)
632 {
633         int ret;
634
635         if (!name)
636                 return -1;
637
638         ret = snprintf(buf, buf_size, ".%s_%s", AU_PREFIX_SYSNOTI, name);
639
640         if (ret >= buf_size)
641                 return -1;
642         else
643                 return 0;
644 }
645
646 gboolean gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
647 {
648         int fd;
649
650         /* need condition check?? */
651
652         fd = g_io_channel_unix_get_fd(src);
653         __handle_event(fd);
654
655         return TRUE;
656 }
657
658 API int heynoti_attach_handler(int fd)
659 {
660         int ret;
661         struct noti_cont *nc = NULL;
662         GSource *src;
663         GIOChannel *gio;
664
665         nc = __get_noti_cont(fd);
666         if (nc == NULL) {
667                 UTIL_ERR("Non-registered file descriptor : %d", fd);
668                 errno = EBADF;
669                 return -1;
670         }
671
672         switch (nc->ht) {
673         case H_NONE:
674                 /* do nothing */
675                 break;
676         case H_GLIB:
677                 UTIL_ERR("glib based handler now in progress");
678                 errno = EINPROGRESS;
679                 break;
680         default:
681                 UTIL_ERR("Another handler already in progress: %d", nc->ht);
682                 errno = EALREADY;
683                 break;
684         }
685         util_retv_if(nc->ht != H_NONE, -1);
686
687         gio = g_io_channel_unix_new(fd);
688         util_retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
689
690         g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
691
692         src = g_io_create_watch(gio, G_IO_IN);
693         g_source_set_callback(src, (GSourceFunc) gio_cb, NULL, NULL);
694         ret = g_source_attach(src, NULL);
695         g_io_channel_unref(gio);
696         g_source_unref(src);
697         if (!ret)
698                 return -1;
699
700         nc->handler = src;
701         nc->ht = H_GLIB;
702
703         return 0;
704 }
705
706 API int heynoti_detach_handler(int fd)
707 {
708         struct noti_cont *nc = NULL;
709
710         nc = __get_noti_cont(fd);
711         if (nc == NULL) {
712                 UTIL_ERR("Non-registered file descriptor : %d", fd);
713                 errno = EBADF;
714                 return -1;
715         }
716
717         switch (nc->ht) {
718         case H_GLIB:
719                 g_source_destroy(nc->handler);
720                 nc->handler = NULL;
721                 nc->ht = H_NONE;
722                 break;
723         default:
724                 /* do nothing ?? */
725                 break;
726         }
727
728         return 0;
729 }
730
731 API void heynoti_close(int fd)
732 {
733         struct noti_cont *r = NULL;
734
735         r = __get_noti_cont(fd);
736         util_warn_if(r == NULL, "Non-registered file descriptor");
737
738         if (r) {
739                 if (r->ht == H_GLIB)
740                         heynoti_detach_handler(fd);
741
742                 __clear_nslot_list(r->g_ns);
743                 g_list_free(r->g_ns);
744                 close(r->fd);
745
746                 g_nc = g_list_remove(g_nc, (gconstpointer) r);
747                 free(r);
748         }
749 }
750