Add multi-user support
[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 /* For multi-user support */
41 #include <tzplatform_config.h>
42
43 #include "heynoti-internal.h"
44
45 #define AU_PREFIX_SYSNOTI "SYS"
46
47 #ifndef NOTI_ROOT
48 #define NOTI_ROOT tzplatform_mkpath(TZ_SYS_SHARE, "noti")
49 #endif
50
51
52 #define NODAC_PERMISSION
53
54 struct noti_slot {
55         int wd;
56         void *cb_data;
57         void (*cb) (void *);
58         uint32_t mask;
59 };
60 typedef struct noti_slot nslot;
61
62 static int __make_noti_root(const char *p);
63 static int __make_noti_file(const char *p);
64 static inline int __make_noti_path(char *path, int size, const char *name);
65 static int __read_proc(const char *path, char *buf, int size);
66 static void __clear_nslot_list(GList *g_ns);
67 static struct noti_cont *__get_noti_cont(int fd);
68 static int __handle_callback(struct noti_cont *nc, int wd, uint32_t mask);
69 static int __handle_event(int fd);
70 static inline int __get_wd(int fd, const char *notipath);
71 static int __add_wd(struct noti_cont *nc, int wd, uint32_t mask,
72                   const char *notipath);
73 static int __add_noti(int fd, const char *notipath, void (*cb) (void *),
74                     void *data, uint32_t mask);
75 static int _del_noti(struct noti_cont *nc, int wd, void (*cb) (void *),
76                      const char *notipath);
77 static int del_noti(int fd, const char *notipath, void (*cb) (void *));
78
79
80 int slot_comp(struct noti_slot *a, struct noti_slot *b)
81 {
82         int r;
83
84         r = a->wd - b->wd;
85         if (r != 0)
86                 return r;
87
88         r = (int)(a->cb - b->cb);
89         return r;
90 }
91
92 typedef enum {
93         H_NONE,
94         H_SIGNAL,
95         H_ECORE,
96         H_GLIB,
97 } htype;
98
99 struct noti_cont {
100         int fd;
101         GList *g_ns;
102
103         htype ht;
104
105         int signo;
106         struct sigaction oldact;
107
108         void *handler;
109 };
110 typedef struct noti_cont ncont;
111
112 static GList *g_nc;
113
114 static int __make_noti_root(const char *p)
115 {
116         int fd;
117
118         if ((fd = open(p, O_RDONLY)) == -1 )
119                 return -1;
120
121         close(fd);
122
123         return 0;
124 }
125
126 static int __make_noti_file(const char *p)
127 {
128         int fd;
129
130         if ((fd = open(p, O_RDONLY)) == -1)
131                 return -1;
132
133         close(fd);
134
135         return 0;
136 }
137
138 static inline int __make_noti_path(char *path, int size, const char *name)
139 {
140         return snprintf(path, size, "%s/%s", NOTI_ROOT, name);
141 }
142
143 static int __read_proc(const char *path, char *buf, int size)
144 {
145         int fd;
146         int ret;
147
148         if (buf == NULL || path == NULL) {
149                 UTIL_DBG("%s: path or buf is NULL\n", __func__);
150                 errno = EINVAL;
151                 return -1;
152         }
153
154         fd = open(path, O_RDONLY);
155         util_retvm_if(fd == -1, -1, "File open error: %s", strerror(errno));
156
157         memset(buf, 0x0, size);
158         ret = read(fd, buf, size);
159         close(fd);
160
161         return ret;
162 }
163
164 #define PROC_VERSION "/proc/version"
165
166 static int __check_kern_ver()
167 {
168         struct utsname nm;
169         int ret;
170         char buf[1024];
171         int v1;
172         int v2;
173         int v3;
174
175         ret = uname(&nm);
176         if (ret == 0) {
177                 strncpy(buf, nm.release, sizeof(buf));
178
179                 ret = sscanf(buf, "%d.%d.%d", &v1, &v2, &v3);
180         } else {
181                 __read_proc(PROC_VERSION, buf, sizeof(buf));
182
183                 ret = sscanf(buf, "Linux version %d.%d.%d", &v1, &v2, &v3);
184         }
185
186         if (ret == 3) {
187                 if(KERNEL_VERSION(v1, v2, v3) < KERNEL_VERSION(2, 6, 13)) {
188                         return -1;
189                 } else {
190                         return 0;
191                 }
192         }
193
194         return -1;
195 }
196
197 static void __clear_nslot_list(GList *g_ns)
198 {
199         struct noti_slot *t;
200         GList *it = NULL;
201
202         for (it = g_ns; it != NULL; it = g_list_next(it)) {
203                 t = (struct noti_slot *)it->data;
204                 free(t);
205         }
206
207 }
208
209 static struct noti_cont *__get_noti_cont(int fd)
210 {
211         struct noti_cont *r = NULL;
212         GList *it;
213
214         for (it = g_nc; it != NULL; it = g_list_next(it)) {
215                 if (it->data) {
216                         r = (struct noti_cont *)it->data;
217                         if (r->fd == fd) {
218                                 break;
219                         } else {
220                                 r = NULL;
221                         }
222                 }
223         }
224
225         return r;
226 }
227
228 static int __handle_callback(struct noti_cont *nc, int wd, uint32_t mask)
229 {
230         struct noti_slot *t;
231         GList *it = NULL;
232
233         if (!nc->g_ns)
234                 return 0;
235
236         for (it = nc->g_ns; it != NULL; it = g_list_next(it)) {
237                 t = (struct noti_slot *)it->data;
238                 if (t->wd == wd) {
239                         if ((mask & t->mask) && t->cb) {
240                                 t->cb(t->cb_data);
241                         }
242                 }
243         }
244
245         return 0;
246 }
247
248 static int __handle_event(int fd)
249 {
250         int r;
251         struct inotify_event ie;
252         char name[FILENAME_MAX] = {0, };
253
254         struct noti_cont *nc;
255
256         nc = __get_noti_cont(fd);
257         util_warn_if(nc == NULL, "Non-registered file descriptor");
258
259         r = read(fd, &ie, sizeof(ie));
260
261         while (r > 0) {
262                 if (nc)
263                         __handle_callback(nc, ie.wd, ie.mask);
264
265                 if(ie.len > SSIZE_MAX)
266                         return -1;
267
268                 if (ie.len > 0u) {
269                         read(fd, name, (ie.len > FILENAME_MAX) ? (size_t)FILENAME_MAX : (size_t) ie.len);
270                 }
271
272                 r = read(fd, &ie, sizeof(ie));
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