install license file
[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                         r = read(fd, name, (ie.len > FILENAME_MAX) ? (size_t)FILENAME_MAX : (size_t) ie.len);
268                 }
269
270                 if(r > 0) {
271                         r = read(fd, &ie, sizeof(ie));
272                 }
273         }
274
275         return 0;
276 }
277
278 API int heynoti_poll_event(int fd)
279 {
280         int r;
281         struct noti_cont *nc;
282         struct pollfd fds[1];
283
284         nc = __get_noti_cont(fd);
285         if (nc == NULL) {
286                 UTIL_ERR("Non-registered file descriptor : %d", fd);
287                 errno = EBADF;
288                 return -1;
289         }
290
291         if (nc->ht != H_NONE) {
292                 UTIL_ERR("Another handler already in progress");
293                 errno = EALREADY;
294                 return -1;
295         }
296
297         fds[0].fd = nc->fd;
298         fds[0].events = POLLIN;
299
300         r = poll(fds, 1, -1);
301         util_retvm_if(r == -1, -1, "Error: poll : %s", strerror(errno));
302
303         if (fds[0].revents & POLLIN)
304                 __handle_event(fd);
305
306         return r;
307 }
308
309 static inline int __get_wd(int fd, const char *notipath)
310 {
311         return inotify_add_watch(fd, notipath, IN_ACCESS);
312 }
313
314 static int __add_wd(struct noti_cont *nc, int wd, uint32_t mask,
315                     const char *notipath)
316 {
317         int r;
318         uint32_t mask_all;
319         struct noti_slot *t;
320         GList *it;
321
322         mask_all = 0;
323         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
324                 t = (struct noti_slot *)it->data;
325                 if (t->wd == wd) {
326                         mask_all |= t->mask;
327                 }
328         }
329
330         mask_all |= mask;
331
332         r = inotify_add_watch(nc->fd, notipath, mask_all);
333         return r;
334 }
335
336 static int __add_noti(int fd, const char *notipath, void (*cb) (void *),
337                       void *data, uint32_t mask)
338 {
339         int r;
340         int wd;
341         struct noti_cont *nc;
342         struct noti_slot t, *n;
343         struct noti_slot *f = NULL;
344         GList *it;
345
346         nc = __get_noti_cont(fd);
347         if (nc == NULL) {
348                 UTIL_DBG("Bad file descriptor");
349                 errno = EBADF;
350                 return -1;
351         }
352
353         wd = __get_wd(fd, notipath);
354         util_retvm_if(wd == -1, -1, "Error: add noti: %s", strerror(errno));
355
356         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
357                 if (it->data) {
358                         f = (struct noti_slot *)it->data;
359                         if (f->wd == wd && f->cb == cb) {
360                                 break;
361                         } else {
362                                 f = NULL;
363                         }
364                 }
365         }
366
367         if (f) {
368                 __add_wd(nc, wd, 0, notipath);
369                 errno = EALREADY;
370                 return -1;
371         }
372
373         r = __add_wd(nc, wd, mask, notipath);
374         util_retvm_if(r == -1, -1, "Error: add noti: %s", strerror(errno));
375
376         n = calloc(1, sizeof(nslot));
377         util_retvm_if(n == NULL, -1, "Error: add noti: %s", strerror(errno));
378
379         n->wd = wd;
380         n->cb_data = data;
381         n->cb = cb;
382         n->mask = mask;
383         nc->g_ns = g_list_append(nc->g_ns, (gpointer) n);
384
385         return 0;
386 }
387
388 API int heynoti_subscribe(int fd, const char *noti, void (*cb) (void *),
389                           void *data)
390 {
391         char notipath[FILENAME_MAX];
392
393         if (noti == NULL || cb == NULL) {
394                 UTIL_DBG("Error: add noti: Invalid input");
395                 errno = EINVAL;
396                 return -1;
397         }
398
399         __make_noti_path(notipath, sizeof(notipath), noti);
400         UTIL_DBG("add watch: [%s]", notipath);
401
402         return __add_noti(fd, notipath, cb, data, IN_CLOSE_WRITE | IN_DELETE);
403 }
404
405 static int _del_noti(struct noti_cont *nc, int wd, void (*cb) (void *),
406                      const char *notipath)
407 {
408         int r = 0;
409         struct noti_slot *t;
410         int n_del;
411         int n_remain;
412         GList *it;
413         GList *it_next;
414
415         n_del = 0;
416         n_remain = 0;
417
418         it = nc->g_ns;
419         while (it) {
420                 it_next = it->next;
421
422                 if (it->data) {
423                         t = (struct noti_slot *)it->data;
424                         if (t->wd == wd) {
425                                 if (cb == NULL || cb == t->cb) {
426                                         nc->g_ns = g_list_remove(nc->g_ns, t);
427                                         free(t);
428                                         n_del++;
429                                 } else {
430                                         n_remain++;
431                                 }
432                         }
433                 }
434
435                 it = it_next;
436         }
437
438         if (n_remain == 0)
439                 return inotify_rm_watch(nc->fd, wd);
440
441         r = __add_wd(nc, wd, 0, notipath);
442
443         if (n_del == 0) {
444                 UTIL_DBG("Error: nothing deleted");
445                 errno = ENOENT;
446                 return -1;
447         }
448
449         return r;
450 }
451
452 static int del_noti(int fd, const char *notipath, void (*cb) (void *))
453 {
454         int wd;
455         struct noti_cont *nc;
456
457         nc = __get_noti_cont(fd);
458         if (nc == NULL) {
459                 UTIL_DBG("Bad file descriptor");
460                 errno = EBADF;
461                 return -1;
462         }
463
464         /* get wd */
465         wd = __get_wd(fd, notipath);
466         util_retv_if(wd == -1, -1);
467
468         return _del_noti(nc, wd, cb, notipath);
469 }
470
471 API int heynoti_unsubscribe(int fd, const char *noti, void (*cb) (void *))
472 {
473         int r;
474         char notipath[FILENAME_MAX];
475
476         if (fd < 0 || noti == NULL) {
477                 errno = EINVAL;
478                 return -1;
479         }
480
481         __make_noti_path(notipath, sizeof(notipath), noti);
482         UTIL_DBG("del watch: [%s]", notipath);
483
484         r = del_noti(fd, notipath, cb);
485         util_warn_if(r == -1, "Error: del [%s]: %s", noti, strerror(errno));
486
487         return r;
488 }
489
490 API int heynoti_publish(const char *noti)
491 {
492         int fd;
493         char *path;
494         char notipath[FILENAME_MAX];
495         struct stat sb;
496
497         if (noti == NULL) {
498                 UTIL_DBG("Error: send noti: Invalid input");
499                 errno = EINVAL;
500                 return -1;
501         }
502
503         path = strstr(noti, "/");
504         if (path) {
505                 snprintf(notipath, sizeof(notipath), "%s", noti);
506         } else {
507                 __make_noti_path(notipath, sizeof(notipath), noti);
508         }
509         UTIL_DBG("send noti: [%s]", notipath);
510
511         fd = open(notipath, O_TRUNC | O_WRONLY);
512
513         util_retvm_if(fd == -1, -1, "Error: send noti: %s", strerror(errno));
514
515         /*
516         fstat(fd, &sb);
517         if(sb.st_uid != getuid())
518                 fchown(fd, getuid(), getgid());
519         if(sb.st_mode & 0033)
520                 fchmod(fd, 0644);
521         */
522
523         close(fd);
524         return 0;
525 }
526
527 API int heynoti_init()
528 {
529         int r;
530         int fd;
531
532         struct noti_cont *nc;
533
534         if(__check_kern_ver() < 0) {
535                 UTIL_ERR("inotify requires kernel version >= 2.6.13 ");
536                 errno = EPERM;
537                 return -1;
538         }
539
540         fd = inotify_init();
541         util_retvm_if(fd == -1, -1, "inotify init: %s", strerror(errno));
542
543         r = fcntl(fd, F_SETFD, FD_CLOEXEC);
544         util_retvm_if(r < 0, -1, "fcntl error : %s", strerror(errno));
545         r = fcntl(fd, F_SETFL, O_NONBLOCK);
546         util_retvm_if(r < 0, -1, "fcntl error : %s", strerror(errno));
547
548         r = __make_noti_root(noti_root);
549         if (r == -1) {
550                 UTIL_ERR("make noti root: %s : %s", noti_root, strerror(errno));
551                 close(fd);
552                 return -1;
553         }
554
555         nc = calloc(1, sizeof(struct noti_cont));
556         if (nc == NULL) {
557                 close(fd);
558                 return -1;
559         }
560
561         nc->fd = fd;
562         /*sglib_ncont_add(&nc_h, nc); */
563         g_nc = g_list_append(g_nc, (gpointer) nc);
564
565         return fd;
566 }
567
568 API int heynoti_get_pnoti_name(pid_t pid, const char *name, char *buf,
569                                int buf_size)
570 {
571         int ret;
572
573         if (!name)
574                 return -1;
575
576         ret = snprintf(buf, buf_size, ".%d_%s", pid, name);
577
578         if (ret >= buf_size)
579                 return -1;
580         else
581                 return 0;
582 }
583
584 API int heynoti_get_snoti_name(const char *name, char *buf, int buf_size)
585 {
586         int ret;
587
588         if (!name)
589                 return -1;
590
591         ret = snprintf(buf, buf_size, ".%s_%s", AU_PREFIX_SYSNOTI, name);
592
593         if (ret >= buf_size)
594                 return -1;
595         else
596                 return 0;
597 }
598
599 gboolean gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
600 {
601         int fd;
602
603         /* need condition check?? */
604
605         fd = g_io_channel_unix_get_fd(src);
606         __handle_event(fd);
607
608         return TRUE;
609 }
610
611 API int heynoti_attach_handler(int fd)
612 {
613         int ret;
614         struct noti_cont *nc = NULL;
615         GSource *src;
616         GIOChannel *gio;
617
618         nc = __get_noti_cont(fd);
619         if (nc == NULL) {
620                 UTIL_ERR("Non-registered file descriptor : %d", fd);
621                 errno = EBADF;
622                 return -1;
623         }
624
625         switch (nc->ht) {
626         case H_NONE:
627                 /* do nothing */
628                 break;
629         case H_GLIB:
630                 UTIL_ERR("glib based handler now in progress");
631                 errno = EINPROGRESS;
632                 break;
633         default:
634                 UTIL_ERR("Another handler already in progress: %d", nc->ht);
635                 errno = EALREADY;
636                 break;
637         }
638         util_retv_if(nc->ht != H_NONE, -1);
639
640         gio = g_io_channel_unix_new(fd);
641         util_retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
642
643         g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
644
645         src = g_io_create_watch(gio, G_IO_IN);
646         g_source_set_callback(src, (GSourceFunc) gio_cb, NULL, NULL);
647         ret = g_source_attach(src, NULL);
648         g_io_channel_unref(gio);
649         g_source_unref(src);
650         if (!ret)
651                 return -1;
652
653         nc->handler = src;
654         nc->ht = H_GLIB;
655
656         return 0;
657 }
658
659 API int heynoti_detach_handler(int fd)
660 {
661         struct noti_cont *nc = NULL;
662
663         nc = __get_noti_cont(fd);
664         if (nc == NULL) {
665                 UTIL_ERR("Non-registered file descriptor : %d", fd);
666                 errno = EBADF;
667                 return -1;
668         }
669
670         switch (nc->ht) {
671         case H_GLIB:
672                 g_source_destroy(nc->handler);
673                 nc->handler = NULL;
674                 nc->ht = H_NONE;
675                 break;
676         default:
677                 /* do nothing ?? */
678                 break;
679         }
680
681         return 0;
682 }
683
684 API void heynoti_close(int fd)
685 {
686         struct noti_cont *r = NULL;
687
688         r = __get_noti_cont(fd);
689         util_warn_if(r == NULL, "Non-registered file descriptor");
690
691         if (r) {
692                 if (r->ht == H_GLIB)
693                         heynoti_detach_handler(fd);
694
695                 __clear_nslot_list(r->g_ns);
696                 g_list_free(r->g_ns);
697                 close(r->fd);
698
699                 g_nc = g_list_remove(g_nc, (gconstpointer) r);
700                 free(r);
701         }
702 }
703