Fix dbus proxy creation code
[platform/core/appfw/slp-pkgmgr.git] / client / src / pkgmgr_client_connection.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include <glib.h>
24 #include <gio/gio.h>
25
26 #include "package-manager.h"
27 #include "pkgmgr_client_debug.h"
28 #include "pkgmgr_client_internal.h"
29 #include "../../installer/pkgmgr_installer.h"
30 #include "../../installer/pkgmgr_installer_config.h"
31
32 #define CONNECTION_RETRY_MAX 5
33 #define CONNECTION_WAIT_USEC (1000000 / 2) /* 0.5 sec */
34 #define REGULAR_USER 5000
35
36 static int _is_system_user(void)
37 {
38         uid_t uid = getuid();
39
40         if (uid < REGULAR_USER)
41                 return 1;
42         else
43                 return 0;
44 }
45
46 static GBusType __get_bus_type(pkgmgr_client_type type)
47 {
48         if (type == PC_REQUEST || _is_system_user())
49                 return G_BUS_TYPE_SYSTEM;
50         else
51                 return G_BUS_TYPE_SESSION;
52 }
53
54 int pkgmgr_client_connection_connect(struct pkgmgr_client_t *pc)
55 {
56         GError *error = NULL;
57         GBusType bus_type;
58
59 #if !GLIB_CHECK_VERSION(2, 35, 0)
60         g_type_init();
61 #endif
62         bus_type = __get_bus_type(pc->pc_type);
63         pc->conn = g_bus_get_sync(bus_type, NULL, &error);
64         if (error) {
65                 ERR("gdbus connection error (%s)", error->message);
66                 g_error_free(error);
67                 return PKGMGR_R_ECOMM;
68         }
69
70         return PKGMGR_R_OK;
71 }
72
73 void pkgmgr_client_connection_disconnect(struct pkgmgr_client_t *pc)
74 {
75         /* flush remaining buffer: blocking mode */
76         g_dbus_connection_flush_sync(pc->conn, NULL, NULL);
77         g_object_unref(pc->conn);
78         pc->conn = NULL;
79 }
80
81 struct signal_map {
82         const char *signal_str;
83         int signal_type;
84 };
85
86 struct signal_map map[] = {
87         {PKGMGR_INSTALLER_INSTALL_EVENT_STR, PKGMGR_CLIENT_STATUS_INSTALL},
88         {PKGMGR_INSTALLER_UNINSTALL_EVENT_STR, PKGMGR_CLIENT_STATUS_UNINSTALL},
89         {PKGMGR_INSTALLER_UPGRADE_EVENT_STR, PKGMGR_CLIENT_STATUS_UPGRADE},
90         {PKGMGR_INSTALLER_CLEAR_EVENT_STR, PKGMGR_CLIENT_STATUS_CLEAR_DATA},
91         {PKGMGR_INSTALLER_MOVE_EVENT_STR, PKGMGR_CLIENT_STATUS_MOVE},
92         {PKGMGR_INSTALLER_INSTALL_PERCENT_KEY_STR,
93                 PKGMGR_CLIENT_STATUS_INSTALL_PROGRESS},
94         {PKGMGR_INSTALLER_GET_SIZE_KEY_STR, PKGMGR_CLIENT_STATUS_GET_SIZE},
95         {PKGMGR_INSTALLER_CLEAR_CACHE_KEY_STR,
96                 PKGMGR_CLIENT_STATUS_CLEAR_CACHE},
97         {PKGMGR_INSTALLER_APP_ENABLE_EVENT_STR,
98                 PKGMGR_CLIENT_STATUS_ENABLE_APP},
99         {PKGMGR_INSTALLER_APP_DISABLE_EVENT_STR,
100                 PKGMGR_CLIENT_STATUS_DISABLE_APP},
101         {PKGMGR_INSTALLER_APP_ENABLE_SPLASH_SCREEN_EVENT_STR,
102                 PKGMGR_CLIENT_STATUS_ENABLE_APP_SPLASH_SCREEN},
103         {PKGMGR_INSTALLER_APP_DISABLE_SPLASH_SCREEN_EVENT_STR,
104                 PKGMGR_CLIENT_STATUS_DISABLE_APP_SPLASH_SCREEN},
105         {NULL, -1}
106 };
107
108 static int __get_signal_type(const char *name)
109 {
110         int i;
111
112         if (name == NULL)
113                 return -1;
114
115         for (i = 0; map[i].signal_str != NULL; i++) {
116                 if (strcmp(map[i].signal_str, name) == 0)
117                         return map[i].signal_type;
118         }
119
120         return -1;
121 }
122
123 static void __handle_size_info_callback(struct cb_info *cb_info,
124                 const char *pkgid, const char *val)
125 {
126         pkg_size_info_t size_info;
127         char buf[BUFMAX];
128         char *saveptr;
129         char *token;
130         pkgmgr_total_pkg_size_info_receive_cb callback;
131
132         snprintf(buf, sizeof(buf), "%s", val);
133
134         DBG("%s, %s", pkgid, val);
135
136         token = strtok_r(buf, ":", &saveptr);
137         if (token == NULL) {
138                 ERR("failed to parse size info");
139                 return;
140         }
141         size_info.data_size = atoll(token);
142         token = strtok_r(NULL, ":", &saveptr);
143         if (token == NULL) {
144                 ERR("failed to parse size info");
145                 return;
146         }
147         size_info.cache_size = atoll(token);
148         token = strtok_r(NULL, ":", &saveptr);
149         if (token == NULL) {
150                 ERR("failed to parse size info");
151                 return;
152         }
153         size_info.app_size = atoll(token);
154         token = strtok_r(NULL, ":", &saveptr);
155         if (token == NULL) {
156                 ERR("failed to parse size info");
157                 return;
158         }
159         size_info.ext_data_size = atoll(token);
160         token = strtok_r(NULL, ":", &saveptr);
161         if (token == NULL) {
162                 ERR("failed to parse size info");
163                 return;
164         }
165         size_info.ext_cache_size = atoll(token);
166         token = strtok_r(NULL, ":", &saveptr);
167         if (token == NULL) {
168                 ERR("failed to parse size info");
169                 return;
170         }
171         size_info.ext_app_size = atoll(token);
172
173         DBG("data: %lld, cache: %lld, app: %lld, ext_data: %lld, "
174                         "ext_cache: %lld, ext_app: %lld",
175                         size_info.data_size, size_info.cache_size,
176                         size_info.app_size, size_info.ext_data_size,
177                         size_info.ext_cache_size, size_info.ext_app_size);
178
179         if (strcmp(pkgid, PKG_SIZE_INFO_TOTAL) == 0) {
180                 callback = (pkgmgr_total_pkg_size_info_receive_cb)
181                         cb_info->size_info_cb;
182                 callback(cb_info->client, &size_info, cb_info->data);
183         } else {
184                 cb_info->size_info_cb(cb_info->client, pkgid, &size_info,
185                                 cb_info->data);
186         }
187 }
188
189 static void __signal_handler(GDBusConnection *conn, const gchar *sender_name,
190                 const gchar *object_path, const gchar *interface_name,
191                 const gchar *signal_name, GVariant *parameters,
192                 gpointer user_data)
193 {
194         uid_t target_uid;
195         char *req_id;
196         char *pkg_type = NULL;
197         char *pkgid = NULL;
198         char *key = NULL;
199         char *val = NULL;
200         char *appid = NULL;
201         int signal_type;
202         struct cb_info *cb_info = (struct cb_info *)user_data;
203
204         g_variant_get(parameters, "(u&s&s&s&s&s&s)",
205                         &target_uid, &req_id, &pkg_type,
206                         &pkgid, &appid, &key, &val);
207
208         /* in case of request, check reqkey */
209         if (cb_info->req_key) {
210                 if (strcmp(cb_info->req_key, req_id) != 0)
211                         return;
212         } else {
213                 signal_type = __get_signal_type(signal_name);
214                 if (signal_type < 0 || !(cb_info->status_type & signal_type))
215                         return;
216         }
217
218         /* each cb_data can only has one callback */
219         if (cb_info->event_cb && strcmp(appid, "") == 0)
220                 cb_info->event_cb(target_uid, cb_info->req_id,
221                                 pkg_type, pkgid, key, val, NULL, cb_info->data);
222         else if (cb_info->app_event_cb && strcmp(appid, "") != 0)
223                 cb_info->app_event_cb(target_uid, cb_info->req_id,
224                                 pkg_type, pkgid, appid, key, val, NULL,
225                                 cb_info->data);
226         else if (cb_info->size_info_cb)
227                 __handle_size_info_callback(cb_info, pkgid, val);
228
229         /* TODO: unsubscribe request callback */
230 }
231
232 int pkgmgr_client_connection_set_callback(struct pkgmgr_client_t *pc,
233                 struct cb_info *cb_info)
234 {
235         cb_info->sid = g_dbus_connection_signal_subscribe(pc->conn, NULL,
236                         PKGMGR_INSTALLER_DBUS_INTERFACE, NULL,
237                         PKGMGR_INSTALLER_DBUS_OBJECT_PATH, NULL,
238                         G_DBUS_SIGNAL_FLAGS_NONE, __signal_handler,
239                         (gpointer)cb_info, NULL);
240         if (!cb_info->sid) {
241                 ERR("failed to subscribe singal");
242                 return PKGMGR_R_ERROR;
243         }
244
245         return PKGMGR_R_OK;
246 }
247
248 void pkgmgr_client_connection_unset_callback(struct pkgmgr_client_t *pc,
249                 struct cb_info *cb_info)
250 {
251         g_dbus_connection_signal_unsubscribe(pc->conn, cb_info->sid);
252 }
253
254 int pkgmgr_client_connection_send_request(struct pkgmgr_client_t *pc,
255                 const char *method, GVariant *params, GVariant **result)
256 {
257         GError *error = NULL;
258         GDBusProxy *proxy;
259         GVariant *r = NULL;
260         int retry_cnt = 0;
261         int ret = PKGMGR_R_ECOMM;
262
263         /* convert floating ref into normal ref */
264         g_variant_ref_sink(params);
265
266         do {
267                 proxy = g_dbus_proxy_new_sync(pc->conn,
268                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
269                                 NULL, PKGMGR_DBUS_SERVICE,
270                                 PKGMGR_DBUS_OBJECT_PATH,
271                                 PKGMGR_DBUS_INTERFACE, NULL, &error);
272                 if (proxy == NULL) {
273                         ERR("failed to get proxy object, sleep and retry[%s]",
274                                         error->message);
275                         g_error_free(error);
276                         error = NULL;
277                         usleep(CONNECTION_WAIT_USEC);
278                         retry_cnt++;
279                         continue;
280                 }
281
282                 r = g_dbus_proxy_call_sync(proxy, method, params,
283                                 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
284                 g_object_unref(proxy);
285                 if (error && error->code == G_DBUS_ERROR_ACCESS_DENIED) {
286                         ERR("failed to send request, privilege denied[%s]",
287                                         error->message);
288                         g_error_free(error);
289                         ret = PKGMGR_R_EPRIV;
290                         break;
291                 }
292                 if (r) {
293                         *result = r;
294                         ret = PKGMGR_R_OK;
295                         break;
296                 }
297
298                 ERR("failed to send request, sleep and retry[%s]",
299                                 error->message);
300                 g_error_free(error);
301                 error = NULL;
302                 usleep(CONNECTION_WAIT_USEC);
303                 retry_cnt++;
304         } while (retry_cnt <= CONNECTION_RETRY_MAX);
305
306         /* decrease ref count to 0 to free resource */
307         g_variant_unref(params);
308
309         return ret;
310 }