Sync add_filter & match for dbus watch
[platform/core/system/system-server.git] / src / core / edbus-handler.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19
20 #include "core/log.h"
21 #include "core/data.h"
22 #include "core/edbus-handler.h"
23 #include "core/common.h"
24 #include "core/devices.h"
25 #include "core/device-notifier.h"
26 #include "core/list.h"
27
28 #define EDBUS_INIT_RETRY_COUNT 5
29 #define NAME_OWNER_CHANGED "NameOwnerChanged"
30 #define NAME_OWNER_MATCH "type='signal',sender='org.freedesktop.DBus',\
31         path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',\
32         member='NameOwnerChanged',arg0='%s'"
33
34
35 struct edbus_list{
36         char *signal_name;
37         E_DBus_Signal_Handler *handler;
38 };
39
40 static struct edbus_object {
41         const char *path;
42         const char *interface;
43         E_DBus_Object *obj;
44         E_DBus_Interface *iface;
45 } edbus_objects[] = {
46         { DEVICED_PATH_CORE   , DEVICED_INTERFACE_CORE   , NULL, NULL },
47         { DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY, NULL, NULL },
48         { DEVICED_PATH_STORAGE, DEVICED_INTERFACE_STORAGE, NULL, NULL },
49         /* Add new object & interface here*/
50 };
51
52 static Eina_List *edbus_handler_list;
53 static Eina_List *edbus_watch_list;
54 static int edbus_init_val;
55 static DBusConnection *conn;
56 static E_DBus_Connection *edbus_conn;
57 static DBusPendingCall *edbus_request_name;
58
59 static int register_edbus_interface(struct edbus_object *object)
60 {
61         int ret;
62
63         if (!object) {
64                 _E("object is invalid value!");
65                 return -1;
66         }
67
68         object->obj = e_dbus_object_add(edbus_conn, object->path, NULL);
69         if (!object->obj) {
70                 _E("fail to add edbus obj");
71                 return -1;
72         }
73
74         object->iface = e_dbus_interface_new(object->interface);
75         if (!object->iface) {
76                 _E("fail to add edbus interface");
77                 return -1;
78         }
79
80         e_dbus_object_interface_attach(object->obj, object->iface);
81
82         return 0;
83 }
84
85 E_DBus_Interface *get_edbus_interface(const char *path)
86 {
87         int i;
88
89         for (i = 0; i < ARRAY_SIZE(edbus_objects); i++)
90                 if (!strcmp(path, edbus_objects[i].path))
91                         return edbus_objects[i].iface;
92
93         return NULL;
94 }
95
96 pid_t get_edbus_sender_pid(DBusMessage *msg)
97 {
98         const char *sender;
99         DBusMessage *send_msg;
100         DBusPendingCall *pending;
101         DBusMessageIter iter;
102         int ret;
103         pid_t pid;
104
105         if (!msg) {
106                 _E("invalid argument!");
107                 return -1;
108         }
109
110         sender = dbus_message_get_sender(msg);
111         if (!sender) {
112                 _E("invalid sender!");
113                 return -1;
114         }
115
116         send_msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
117                                     DBUS_PATH_DBUS,
118                                     DBUS_INTERFACE_DBUS,
119                                     "GetConnectionUnixProcessID");
120         if (!send_msg) {
121                 _E("invalid send msg!");
122                 return -1;
123         }
124
125         ret = dbus_message_append_args(send_msg, DBUS_TYPE_STRING,
126                                     &sender, DBUS_TYPE_INVALID);
127         if (!ret) {
128                 _E("fail to append args!");
129                 dbus_message_unref(send_msg);
130                 return -1;
131         }
132
133         pending = e_dbus_message_send(edbus_conn, send_msg, NULL, -1, NULL);
134         if (!pending) {
135                 _E("pending is null!");
136                 dbus_message_unref(send_msg);
137                 return -1;
138         }
139
140         dbus_message_unref(send_msg);
141
142         /* block until reply is received */
143         dbus_pending_call_block(pending);
144
145         msg = dbus_pending_call_steal_reply(pending);
146         dbus_pending_call_unref(pending);
147         if (!msg) {
148                 _E("reply msg is null!");
149                 return -1;
150         }
151
152         dbus_message_iter_init(msg, &iter);
153         dbus_message_iter_get_basic(&iter, &pid);
154         dbus_message_unref(msg);
155
156         return pid;
157 }
158
159 static void unregister_edbus_signal_handle(void)
160 {
161         Eina_List *tmp;
162         Eina_List *tmp_next;
163         struct edbus_list *entry;
164
165         EINA_LIST_FOREACH_SAFE(edbus_handler_list, tmp, tmp_next, entry) {
166                 if (entry != NULL) {
167                         e_dbus_signal_handler_del(edbus_conn, entry->handler);
168                         edbus_handler_list = eina_list_remove(edbus_handler_list, entry);
169                         free(entry->signal_name);
170                         free(entry);
171                 }
172         }
173 }
174
175 int register_edbus_signal_handler(const char *path, const char *interface,
176                 const char *name, E_DBus_Signal_Cb cb)
177 {
178         Eina_List *tmp;
179         struct edbus_list *entry;
180         E_DBus_Signal_Handler *handler;
181
182         EINA_LIST_FOREACH(edbus_handler_list, tmp, entry) {
183                 if (entry != NULL && strncmp(entry->signal_name, name, strlen(name)) == 0)
184                         return -1;
185         }
186
187         handler = e_dbus_signal_handler_add(edbus_conn, NULL, path,
188                                 interface, name, cb, NULL);
189
190         if (!handler) {
191                 _E("fail to add edbus handler");
192                 return -1;
193         }
194
195         _E("add edbus service: %s", name);
196
197         entry = malloc(sizeof(struct edbus_list));
198
199         if (!entry) {
200                 _E("Malloc failed");
201                 return -1;
202         }
203
204         entry->signal_name = strndup(name, strlen(name));
205
206         if (!entry->signal_name) {
207                 _E("Malloc failed");
208                 free(entry);
209                 return -1;
210         }
211
212         entry->handler = handler;
213         edbus_handler_list = eina_list_prepend(edbus_handler_list, entry);
214         if (!edbus_handler_list) {
215                 _E("eina_list_prepend failed");
216                 free(entry->signal_name);
217                 free(entry);
218                 return -1;
219         }
220         return 0;
221 }
222
223 int broadcast_edbus_signal(const char *path, const char *interface,
224                 const char *name, int type, void *value)
225 {
226         DBusMessage *signal;
227         DBusMessageIter iter;
228         DBusMessageIter val;
229         char sig[2] = {type, '\0'};
230
231         signal = dbus_message_new_signal(path, interface, name);
232         if (!signal) {
233                 _E("fail to allocate new %s.%s signal", interface, name);
234                 return -1;
235         }
236
237         dbus_message_append_args(signal, type, value, DBUS_TYPE_INVALID);
238
239         e_dbus_message_send(edbus_conn, signal, NULL, -1, NULL);
240
241         dbus_message_unref(signal);
242         return 0;
243 }
244
245 static DBusHandlerResult message_filter(DBusConnection *connection,
246                 DBusMessage *message, void *data)
247 {
248         char match[256];
249         int ret;
250         const char *iface, *member, *watch, *arg = NULL;
251         Eina_List *l;
252
253         if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
254                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
255
256         iface = dbus_message_get_interface(message);
257         member = dbus_message_get_member(message);
258
259         if (strcmp(iface, DBUS_INTERFACE_DBUS))
260                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
261
262         if (strcmp(member, NAME_OWNER_CHANGED))
263                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
264
265         ret = dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg,
266                     DBUS_TYPE_INVALID);
267         if (!ret) {
268                 _E("no message");
269                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
270         }
271
272         _D("Argument : %s", arg);
273
274         EINA_LIST_FOREACH(edbus_watch_list, l, watch) {
275                 if (strcmp(arg, watch)) continue;
276
277                 /* notify 'process terminated' to device notifiers */
278                 device_notify(DEVICE_NOTIFIER_PROCESS_TERMINATED, watch);
279
280                 /* remove registered sender */
281                 snprintf(match, sizeof(match), NAME_OWNER_MATCH, watch);
282                 dbus_bus_remove_match(conn, match, NULL);
283                 EINA_LIST_REMOVE(edbus_watch_list, watch);
284                 free(watch);
285                 break;
286         }
287
288         if (eina_list_count(edbus_watch_list) == 0) {
289                 dbus_connection_remove_filter(conn, message_filter, NULL);
290                 _I("remove message filter, no watcher!");
291         }
292
293         return DBUS_HANDLER_RESULT_HANDLED;
294 }
295
296 int register_edbus_watch(DBusMessage *msg)
297 {
298         char match[256];
299         const char *sender, *watch;
300         Eina_List *l;
301         int ret;
302
303         if (!msg) {
304                 _E("invalid argument!");
305                 return -EINVAL;
306         }
307
308         sender = dbus_message_get_sender(msg);
309         if (!sender) {
310                 _E("invalid sender!");
311                 return -EINVAL;
312         }
313
314         /* check the sender is already registered */
315         EINA_LIST_FOREACH(edbus_watch_list, l, watch) {
316                 if (strcmp(sender, watch)) continue;
317
318                 _I("%s is already watched!", watch);
319                 return 0;
320         }
321
322         watch = strndup(sender, strlen(sender));
323         if (!watch) {
324                 _E("Malloc failed");
325                 return -ENOMEM;
326         }
327
328         /* Add message filter */
329         if (eina_list_count(edbus_watch_list) == 0) {
330                 ret = dbus_connection_add_filter(conn, message_filter, NULL, NULL);
331                 if (!ret) {
332                         _E("fail to add message filter!");
333                         free(watch);
334                         return -ENOMEM;
335                 }
336                 _I("success to add message filter!");
337         }
338
339         /* Add sender to watch list */
340         EINA_LIST_APPEND(edbus_watch_list, watch);
341
342         snprintf(match, sizeof(match), NAME_OWNER_MATCH, watch);
343         dbus_bus_add_match(conn, match, NULL);
344
345         _I("%s is watched by dbus!", watch);
346 }
347
348 static unregister_edbus_watch_all(void)
349 {
350         char match[256];
351         Eina_List *n, *next;
352         struct edbus_list *watch;
353
354         EINA_LIST_FOREACH_SAFE(edbus_watch_list, n, next, watch) {
355                 snprintf(match, sizeof(match), NAME_OWNER_MATCH, watch);
356                 dbus_bus_remove_match(conn, match, NULL);
357                 EINA_LIST_REMOVE(edbus_watch_list, watch);
358                 free(watch);
359         }
360 }
361
362 static void edbus_init(void *data)
363 {
364         int retry = EDBUS_INIT_RETRY_COUNT;
365         int i, r;
366
367         while (--retry) {
368                 edbus_init_val = e_dbus_init();
369                 if (edbus_init_val)
370                         break;
371                 if (!retry) {
372                         _E("fail to init edbus");
373                         return;
374                 }
375         }
376
377         retry = EDBUS_INIT_RETRY_COUNT;
378         while (--retry) {
379                 edbus_conn = e_dbus_bus_get(DBUS_BUS_SYSTEM);
380                 if (edbus_conn)
381                         break;
382                 if (!retry) {
383                         _E("fail to get edbus");
384                         goto err_dbus_shutdown;
385                 }
386         }
387
388         retry = EDBUS_INIT_RETRY_COUNT;
389         while (--retry) {
390                 edbus_request_name = e_dbus_request_name(edbus_conn, BUS_NAME, 0, NULL, NULL);
391                 if (edbus_request_name)
392                         break;
393                 if (!retry) {
394                         _E("fail to request edbus name");
395                         goto err_dbus_close;
396                 }
397         }
398
399         for (i = 0; i < ARRAY_SIZE(edbus_objects); i++) {
400                 r = register_edbus_interface(&edbus_objects[i]);
401                 if (r < 0)
402                         _E("fail to add obj & interface for %s",
403                                     edbus_objects[i].interface);
404
405                 _I("add new obj for %s", edbus_objects[i].interface);
406         }
407         return;
408
409 err_dbus_close:
410         e_dbus_connection_close(edbus_conn);
411 err_dbus_shutdown:
412         e_dbus_shutdown();
413         return;
414 }
415
416 static void edbus_exit(void *data)
417 {
418         unregister_edbus_signal_handle();
419         dbus_connection_remove_filter(conn, message_filter, NULL);
420         unregister_edbus_watch_all();
421         e_dbus_connection_close(edbus_conn);
422         e_dbus_shutdown();
423 }
424
425 const struct device_ops edbus_device_ops = {
426         .init = edbus_init,
427         .exit = edbus_exit,
428 };