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