a2e161f1fd02bcbd8970b2bb1a16270b7caca64d
[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 "package-manager-types.h"
28 #include "pkgmgr_client_debug.h"
29 #include "pkgmgr_client_internal.h"
30 #include "../../installer/pkgmgr_installer.h"
31 #include "../../installer/pkgmgr_installer_config.h"
32
33 #define CONNECTION_RETRY_MAX 5
34 #define CONNECTION_WAIT_USEC (1000000 / 2) /* 0.5 sec */
35 #define CONNECTION_TIMEOUT_MSEC 5000 /* 5 sec */
36 #define REGULAR_USER 5000
37
38 static int _is_system_user(void)
39 {
40         uid_t uid = getuid();
41
42         if (uid < REGULAR_USER)
43                 return 1;
44         else
45                 return 0;
46 }
47
48 static GBusType __get_bus_type(pkgmgr_client_type type)
49 {
50         if (type == PC_REQUEST || _is_system_user())
51                 return G_BUS_TYPE_SYSTEM;
52         else
53                 return G_BUS_TYPE_SESSION;
54 }
55
56 int pkgmgr_client_connection_connect(struct pkgmgr_client_t *pc)
57 {
58         GError *error = NULL;
59         GBusType bus_type;
60
61 #if !GLIB_CHECK_VERSION(2, 35, 0)
62         g_type_init();
63 #endif
64         bus_type = __get_bus_type(pc->pc_type);
65         pc->conn = g_bus_get_sync(bus_type, NULL, &error);
66         if (error) {
67                 ERR("gdbus connection error (%s)", error->message);
68                 g_error_free(error);
69                 return PKGMGR_R_ECOMM;
70         }
71
72         return PKGMGR_R_OK;
73 }
74
75 void pkgmgr_client_connection_disconnect(struct pkgmgr_client_t *pc)
76 {
77         /* flush remaining buffer: blocking mode */
78         g_dbus_connection_flush_sync(pc->conn, NULL, NULL);
79         g_object_unref(pc->conn);
80         pc->conn = NULL;
81 }
82
83 struct signal_map {
84         const char *signal_str;
85         int signal_type;
86 };
87
88 struct signal_map map[] = {
89         {PKGMGR_INSTALLER_INSTALL_EVENT_STR, PKGMGR_CLIENT_STATUS_INSTALL},
90         {PKGMGR_INSTALLER_UNINSTALL_EVENT_STR, PKGMGR_CLIENT_STATUS_UNINSTALL},
91         {PKGMGR_INSTALLER_UPGRADE_EVENT_STR, PKGMGR_CLIENT_STATUS_UPGRADE},
92         {PKGMGR_INSTALLER_CLEAR_EVENT_STR, PKGMGR_CLIENT_STATUS_CLEAR_DATA},
93         {PKGMGR_INSTALLER_MOVE_EVENT_STR, PKGMGR_CLIENT_STATUS_MOVE},
94         {PKGMGR_INSTALLER_INSTALL_PERCENT_KEY_STR,
95                 PKGMGR_CLIENT_STATUS_INSTALL_PROGRESS},
96         {PKGMGR_INSTALLER_GET_SIZE_KEY_STR, PKGMGR_CLIENT_STATUS_GET_SIZE},
97         {PKGMGR_INSTALLER_CLEAR_CACHE_KEY_STR,
98                 PKGMGR_CLIENT_STATUS_CLEAR_CACHE},
99         {PKGMGR_INSTALLER_APP_ENABLE_EVENT_STR,
100                 PKGMGR_CLIENT_STATUS_ENABLE_APP},
101         {PKGMGR_INSTALLER_APP_DISABLE_EVENT_STR,
102                 PKGMGR_CLIENT_STATUS_DISABLE_APP},
103         {PKGMGR_INSTALLER_APP_ENABLE_SPLASH_SCREEN_EVENT_STR,
104                 PKGMGR_CLIENT_STATUS_ENABLE_APP_SPLASH_SCREEN},
105         {PKGMGR_INSTALLER_APP_DISABLE_SPLASH_SCREEN_EVENT_STR,
106                 PKGMGR_CLIENT_STATUS_DISABLE_APP_SPLASH_SCREEN},
107         {PKGMGR_INSTALLER_RES_COPY_EVENT_STR, PKGMGR_CLIENT_STATUS_RES_COPY},
108         {PKGMGR_INSTALLER_RES_CREATE_DIR_EVENT_STR,
109                 PKGMGR_CLIENT_STATUS_RES_CREATE_DIR},
110         {PKGMGR_INSTALLER_RES_REMOVE_EVENT_STR,
111                 PKGMGR_CLIENT_STATUS_RES_REMOVE},
112         {PKGMGR_INSTALLER_RES_UNINSTALL_EVENT_STR,
113                 PKGMGR_CLIENT_STATUS_RES_UNINSTALL},
114         {NULL, -1}
115 };
116
117 static int __get_signal_type(const char *name)
118 {
119         int i;
120
121         if (name == NULL)
122                 return -1;
123
124         for (i = 0; map[i].signal_str != NULL; i++) {
125                 if (strcmp(map[i].signal_str, name) == 0)
126                         return map[i].signal_type;
127         }
128
129         return -1;
130 }
131
132 static void __handle_size_info_callback(struct cb_info *cb_info,
133                 const char *pkgid, const char *val)
134 {
135         pkg_size_info_t size_info;
136         char buf[BUFMAX];
137         char *saveptr;
138         char *token;
139         pkgmgr_total_pkg_size_info_receive_cb callback;
140
141         snprintf(buf, sizeof(buf), "%s", val);
142
143         DBG("%s, %s", pkgid, val);
144
145         token = strtok_r(buf, ":", &saveptr);
146         if (token == NULL) {
147                 ERR("failed to parse size info");
148                 return;
149         }
150         size_info.data_size = atoll(token);
151         token = strtok_r(NULL, ":", &saveptr);
152         if (token == NULL) {
153                 ERR("failed to parse size info");
154                 return;
155         }
156         size_info.cache_size = atoll(token);
157         token = strtok_r(NULL, ":", &saveptr);
158         if (token == NULL) {
159                 ERR("failed to parse size info");
160                 return;
161         }
162         size_info.app_size = atoll(token);
163         token = strtok_r(NULL, ":", &saveptr);
164         if (token == NULL) {
165                 ERR("failed to parse size info");
166                 return;
167         }
168         size_info.ext_data_size = atoll(token);
169         token = strtok_r(NULL, ":", &saveptr);
170         if (token == NULL) {
171                 ERR("failed to parse size info");
172                 return;
173         }
174         size_info.ext_cache_size = atoll(token);
175         token = strtok_r(NULL, ":", &saveptr);
176         if (token == NULL) {
177                 ERR("failed to parse size info");
178                 return;
179         }
180         size_info.ext_app_size = atoll(token);
181
182         DBG("data: %lld, cache: %lld, app: %lld, ext_data: %lld, "
183                         "ext_cache: %lld, ext_app: %lld",
184                         size_info.data_size, size_info.cache_size,
185                         size_info.app_size, size_info.ext_data_size,
186                         size_info.ext_cache_size, size_info.ext_app_size);
187
188         if (strcmp(pkgid, PKG_SIZE_INFO_TOTAL) == 0) {
189                 callback = (pkgmgr_total_pkg_size_info_receive_cb)
190                         cb_info->size_info_cb;
191                 callback(cb_info->client, &size_info, cb_info->data);
192         } else {
193                 cb_info->size_info_cb(cb_info->client, pkgid, &size_info,
194                                 cb_info->data);
195         }
196 }
197
198 static void __handle_pkg_signal(const gchar *signal_name,
199                 GVariant *parameters, gpointer user_data)
200 {
201         uid_t target_uid;
202         char *req_id;
203         char *pkg_type = NULL;
204         char *appid = NULL;
205         char *pkgid = NULL;
206         char *key = NULL;
207         char *val = NULL;
208         struct cb_info *cb_info = (struct cb_info *)user_data;
209         GVariantIter *iter = NULL;
210         int signal_type;
211
212         g_variant_get(parameters, "(u&sa(sss)&s&s)", &target_uid, &req_id, &iter,
213                         &key, &val);
214         while (g_variant_iter_loop(iter, "(&s&s&s)", &pkgid, &appid, &pkg_type)) {
215                 if (cb_info->req_key) {
216                         if (strcmp(cb_info->req_key, req_id) != 0)
217                                 continue;
218                 } else {
219                         signal_type = __get_signal_type(signal_name);
220                         if (signal_type < 0 || !(cb_info->status_type & signal_type)) {
221                                 g_variant_iter_free(iter);
222                                 return;
223                         }
224                 }
225
226                 /* each cb_data can only has one callback */
227                 if (cb_info->event_cb) {
228                         cb_info->event_cb(target_uid, cb_info->req_id,
229                                         pkg_type, pkgid, key, val, NULL, cb_info->data);
230                 } else if (cb_info->app_event_cb && strcmp(appid, "") != 0) {
231                         cb_info->app_event_cb(target_uid, cb_info->req_id,
232                                         pkg_type, pkgid, appid, key, val, NULL,
233                                         cb_info->data);
234                 } else if (cb_info->size_info_cb) {
235                         __handle_size_info_callback(cb_info, pkgid, val);
236                 }
237
238                 /* TODO: unsubscribe request callback */
239         }
240         g_variant_iter_free(iter);
241 }
242
243 static void __handle_res_copy_event_signal(const gchar *signal_name,
244                 GVariant *parameters, gpointer user_data)
245 {
246         uid_t target_uid;
247         char *req_id;
248         char *pkgid = NULL;
249         char *status = NULL;
250         struct cb_info *cb_info = (struct cb_info *)user_data;
251         int signal_type;
252         GVariant *extra_param = NULL;
253         pkgmgr_res_event_info_t event_info;
254
255         if (!cb_info->res_copy_event_cb)
256                 return;
257
258         g_variant_get(parameters, "(u&s&s&sv)", &target_uid, &req_id, &pkgid, &status, &extra_param);
259         if (!g_variant_type_equal(G_VARIANT_TYPE("(i)"),
260                         g_variant_get_type(extra_param))) {
261                 ERR("invalid extra parameter");
262                 g_variant_unref(extra_param);
263                 return;
264         }
265         if (cb_info->req_key) {
266                 if (strcmp(cb_info->req_key, req_id) != 0) {
267                         g_variant_unref(extra_param);
268                         return;
269                 }
270         } else {
271                 signal_type = __get_signal_type(signal_name);
272                 if (signal_type < 0 || !(cb_info->status_type & signal_type)) {
273                         g_variant_unref(extra_param);
274                         return;
275                 }
276         }
277
278         g_variant_get(extra_param, "(i)", &event_info.error_code);
279         cb_info->res_copy_event_cb(target_uid, cb_info->req_id, pkgid, signal_name,
280                         status, &event_info, cb_info->data);
281         g_variant_unref(extra_param);
282 }
283
284 static void __signal_handler(GDBusConnection *conn, const gchar *sender_name,
285                 const gchar *object_path, const gchar *interface_name,
286                 const gchar *signal_name, GVariant *parameters,
287                 gpointer user_data)
288 {
289         if (g_variant_type_equal(G_VARIANT_TYPE("(usa(sss)ss)"),
290                         g_variant_get_type(parameters))) {
291                 __handle_pkg_signal(signal_name, parameters, user_data);
292         } else if (!strcmp(signal_name, PKGMGR_INSTALLER_RES_COPY_EVENT_STR) ||
293                         !strcmp(signal_name, PKGMGR_INSTALLER_RES_REMOVE_EVENT_STR) ||
294                         !strcmp(signal_name, PKGMGR_INSTALLER_RES_UNINSTALL_EVENT_STR) ||
295                         !strcmp(signal_name, PKGMGR_INSTALLER_RES_CREATE_DIR_EVENT_STR)) {
296                 __handle_res_copy_event_signal(signal_name, parameters, user_data);
297         }
298 }
299
300 static void __set_signal_list(int event, GList **signal_list)
301 {
302         int i;
303         if (event == PKGMGR_CLIENT_STATUS_ALL)
304                 return;
305
306         for (i = 0; map[i].signal_str != NULL; i++) {
307                 if (event & map[i].signal_type)
308                         *signal_list = g_list_append(*signal_list,
309                                         (char *)map[i].signal_str);
310         }
311 }
312
313 static int __subscribe_signal(struct pkgmgr_client_t *pc,
314                 struct cb_info *cb_info, const char *signal)
315 {
316         guint sid = g_dbus_connection_signal_subscribe(pc->conn, NULL,
317                         PKGMGR_INSTALLER_DBUS_INTERFACE, signal,
318                         PKGMGR_INSTALLER_DBUS_OBJECT_PATH, NULL,
319                         G_DBUS_SIGNAL_FLAGS_NONE, __signal_handler,
320                         (gpointer)cb_info, NULL);
321         if (!sid) {
322                 ERR("failed to subscribe singal");
323                 return PKGMGR_R_ERROR;
324         }
325         cb_info->sid_list = g_list_append(cb_info->sid_list,
326                         GUINT_TO_POINTER(sid));
327
328         return PKGMGR_R_OK;
329 }
330
331 int pkgmgr_client_connection_set_callback(struct pkgmgr_client_t *pc,
332                 struct cb_info *cb_info)
333 {
334         GList *signal_list = NULL;
335         GList *tmp_list = NULL;
336         int ret;
337         char *signal_type;
338         __set_signal_list(pc->status_type, &signal_list);
339
340         if (g_list_length(signal_list) == 0)
341                 return __subscribe_signal(pc, cb_info, NULL);
342
343         for (tmp_list = signal_list; tmp_list != NULL;
344                         tmp_list = g_list_next(tmp_list)) {
345                 signal_type = (char *)tmp_list->data;
346                 ret = __subscribe_signal(pc, cb_info, signal_type);
347                 if (ret != 0) {
348                         g_list_free(signal_list);
349                         return PKGMGR_R_ERROR;
350                 }
351         }
352
353         g_list_free(signal_list);
354         return PKGMGR_R_OK;
355 }
356
357 void pkgmgr_client_connection_unset_callback(struct pkgmgr_client_t *pc,
358                 struct cb_info *cb_info)
359 {
360         GList *tmp = NULL;
361         guint tmp_sid;
362         for (tmp = cb_info->sid_list; tmp != NULL; tmp = g_list_next(tmp)) {
363                 tmp_sid = GPOINTER_TO_UINT(tmp->data);
364                 g_dbus_connection_signal_unsubscribe(pc->conn, tmp_sid);
365         }
366 }
367
368 int pkgmgr_client_connection_send_request(struct pkgmgr_client_t *pc,
369                 const char *method, GVariant *params, GVariant **result)
370 {
371         GError *error = NULL;
372         GDBusProxy *proxy;
373         GVariant *r = NULL;
374         int retry_cnt = 0;
375         int ret = PKGMGR_R_ECOMM;
376
377         /* convert floating ref into normal ref */
378         g_variant_ref_sink(params);
379
380         do {
381                 proxy = g_dbus_proxy_new_sync(pc->conn,
382                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
383                                 NULL, PKGMGR_DBUS_SERVICE,
384                                 PKGMGR_DBUS_OBJECT_PATH,
385                                 PKGMGR_DBUS_INTERFACE, NULL, &error);
386                 if (proxy == NULL) {
387                         ERR("failed to get proxy object, sleep and retry[%s]",
388                                         error->message);
389                         g_error_free(error);
390                         error = NULL;
391                         usleep(CONNECTION_WAIT_USEC);
392                         retry_cnt++;
393                         continue;
394                 }
395
396                 r = g_dbus_proxy_call_sync(proxy, method, params,
397                                 G_DBUS_CALL_FLAGS_NONE,
398                                 CONNECTION_TIMEOUT_MSEC, NULL, &error);
399                 g_object_unref(proxy);
400                 if (error && error->code == G_DBUS_ERROR_ACCESS_DENIED) {
401                         ERR("failed to send request, privilege denied[%s]",
402                                         error->message);
403                         g_error_free(error);
404                         ret = PKGMGR_R_EPRIV;
405                         break;
406                 }
407                 if (r) {
408                         *result = r;
409                         ret = PKGMGR_R_OK;
410                         break;
411                 }
412
413                 ERR("failed to send request, sleep and retry[%s]",
414                                 error->message);
415                 g_error_free(error);
416                 error = NULL;
417                 usleep(CONNECTION_WAIT_USEC);
418                 retry_cnt++;
419         } while (retry_cnt <= CONNECTION_RETRY_MAX);
420
421         /* decrease ref count to 0 to free resource */
422         g_variant_unref(params);
423
424         return ret;
425 }