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