device: Merge public api on Tizen 2.3 into tizen branch
[platform/core/api/device.git] / src / dbus.c
1 /*
2  * system-dbus
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 <stdio.h>
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <errno.h>
24 #include <dbus/dbus-glib-lowlevel.h>
25
26 #include "common.h"
27 #include "dbus.h"
28
29 #define DBUS_REPLY_TIMEOUT      (-1)
30
31 struct pending_call_data {
32         dbus_pending_cb func;
33         void *data;
34 };
35
36 static int append_variant(DBusMessageIter *iter, const char *sig, char *param[])
37 {
38         char *ch;
39         int i;
40         int int_type;
41         uint64_t int64_type;
42
43         if (!sig || !param)
44                 return 0;
45
46         for (ch = (char*)sig, i = 0; *ch != '\0'; ++i, ++ch) {
47                 switch (*ch) {
48                 case 'i':
49                         int_type = atoi(param[i]);
50                         dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &int_type);
51                         break;
52                 case 'u':
53                         int_type = strtoul(param[i], NULL, 10);
54                         dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &int_type);
55                         break;
56                 case 't':
57                         int64_type = atoll(param[i]);
58                         dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &int64_type);
59                         break;
60                 case 's':
61                         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &param[i]);
62                         break;
63                 default:
64                         return -EINVAL;
65                 }
66         }
67
68         return 0;
69 }
70
71 int dbus_method_sync(const char *dest, const char *path,
72                 const char *interface, const char *method,
73                 const char *sig, char *param[])
74 {
75         DBusConnection *conn;
76         DBusMessage *msg;
77         DBusMessageIter iter;
78         DBusMessage *reply;
79         DBusError err;
80         int ret, result;
81
82         conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
83         if (!conn) {
84                 _E("dbus_bus_get error");
85                 return -EPERM;
86         }
87
88         msg = dbus_message_new_method_call(dest, path, interface, method);
89         if (!msg) {
90                 _E("dbus_message_new_method_call(%s:%s-%s)",
91                         path, interface, method);
92                 return -EBADMSG;
93         }
94
95         dbus_message_iter_init_append(msg, &iter);
96         ret = append_variant(&iter, sig, param);
97         if (ret < 0) {
98                 _E("append_variant error(%d) %s %s:%s-%s",
99                         ret, dest, path, interface, method);
100                 dbus_message_unref(msg);
101                 return ret;
102         }
103
104         dbus_error_init(&err);
105
106         reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_REPLY_TIMEOUT, &err);
107         dbus_message_unref(msg);
108         if (!reply) {
109                 _E("dbus_connection_send error(%s:%s) %s %s:%s-%s",
110                         err.name, err.message, dest, path, interface, method);
111                 dbus_error_free(&err);
112                 return -ECOMM;
113         }
114
115         ret = dbus_message_get_args(reply, &err, DBUS_TYPE_INT32, &result, DBUS_TYPE_INVALID);
116         dbus_message_unref(reply);
117         if (!ret) {
118                 _E("no message : [%s:%s] %s %s:%s-%s",
119                         err.name, err.message, dest, path, interface, method);
120                 dbus_error_free(&err);
121                 return -ENOMSG;
122         }
123
124         return result;
125 }
126
127 static void cb_pending(DBusPendingCall *pending, void *user_data)
128 {
129         DBusMessage *msg;
130         DBusError err;
131         struct pending_call_data *data = user_data;
132         int ret;
133
134         ret = dbus_pending_call_get_completed(pending);
135         if (!ret) {
136                 _I("dbus_pending_call_get_completed() fail");
137                 dbus_pending_call_unref(pending);
138                 return;
139         }
140
141         dbus_error_init(&err);
142         msg = dbus_pending_call_steal_reply(pending);
143         if (!msg) {
144                 _E("no message : [%s:%s]", err.name, err.message);
145
146                 if (data->func) {
147                         dbus_set_error(&err, "org.tizen.system.deviced.NoReply",
148                                         "There was no reply to this method call");
149                         data->func(data->data, NULL, &err);
150                         dbus_error_free(&err);
151                 }
152                 return;
153         }
154
155         ret = dbus_set_error_from_message(&err, msg);
156         if (ret) {
157                 _E("error msg : [%s:%s]", err.name, err.message);
158
159                 if (data->func)
160                         data->func(data->data, NULL, &err);
161                 dbus_error_free(&err);
162         } else {
163                 if (data->func)
164                         data->func(data->data, msg, &err);
165         }
166
167         dbus_message_unref(msg);
168         dbus_pending_call_unref(pending);
169 }
170
171 int dbus_method_async_with_reply(const char *dest, const char *path,
172                 const char *interface, const char *method,
173                 const char *sig, char *param[], dbus_pending_cb cb, int timeout, void *data)
174 {
175         DBusConnection *conn;
176         DBusMessage *msg;
177         DBusMessageIter iter;
178         DBusPendingCall *pending = NULL;
179         struct pending_call_data *pdata;
180         int ret;
181
182         conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
183         if (!conn) {
184                 _E("dbus_bus_get error");
185                 return -EPERM;
186         }
187
188         /* this function should be invoked to receive dbus messages
189          * does nothing if it's already been done */
190         dbus_connection_setup_with_g_main(conn, NULL);
191
192         msg = dbus_message_new_method_call(dest, path, interface, method);
193         if (!msg) {
194                 _E("dbus_message_new_method_call(%s:%s-%s)",
195                         path, interface, method);
196                 return -EBADMSG;
197         }
198
199         dbus_message_iter_init_append(msg, &iter);
200         ret = append_variant(&iter, sig, param);
201         if (ret < 0) {
202                 _E("append_variant error(%d)%s %s:%s-%s",
203                         ret, dest, path, interface, method);
204                 dbus_message_unref(msg);
205                 return ret;
206         }
207
208         ret = dbus_connection_send_with_reply(conn, msg, &pending, timeout);
209         if (!ret) {
210                 dbus_message_unref(msg);
211                 _E("dbus_connection_send error(%s %s:%s-%s)",
212                         dest, path, interface, method);
213                 return -ECOMM;
214         }
215
216         dbus_message_unref(msg);
217
218         if (cb && pending) {
219                 pdata = malloc(sizeof(struct pending_call_data));
220                 if (!pdata)
221                         return -ENOMEM;
222
223                 pdata->func = cb;
224                 pdata->data = data;
225
226                 ret = dbus_pending_call_set_notify(pending, cb_pending, pdata, free);
227                 if (!ret) {
228                         free(pdata);
229                         dbus_pending_call_cancel(pending);
230                         return -ECOMM;
231                 }
232         }
233
234         return 0;
235 }