17372fd4c1ec653f0ec2468c40ea8e75493c275e
[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         {NULL, -1}
107 };
108
109 static void __handle_size_info_callback(struct cb_info *cb_info,
110                 const char *pkgid, const char *val)
111 {
112         pkg_size_info_t size_info;
113         char buf[BUFMAX];
114         char *saveptr;
115         char *token;
116         pkgmgr_total_pkg_size_info_receive_cb callback;
117
118         snprintf(buf, sizeof(buf), "%s", val);
119
120         DBG("%s, %s", pkgid, val);
121
122         token = strtok_r(buf, ":", &saveptr);
123         if (token == NULL) {
124                 ERR("failed to parse size info");
125                 return;
126         }
127         size_info.data_size = atoll(token);
128         token = strtok_r(NULL, ":", &saveptr);
129         if (token == NULL) {
130                 ERR("failed to parse size info");
131                 return;
132         }
133         size_info.cache_size = atoll(token);
134         token = strtok_r(NULL, ":", &saveptr);
135         if (token == NULL) {
136                 ERR("failed to parse size info");
137                 return;
138         }
139         size_info.app_size = atoll(token);
140         token = strtok_r(NULL, ":", &saveptr);
141         if (token == NULL) {
142                 ERR("failed to parse size info");
143                 return;
144         }
145         size_info.ext_data_size = atoll(token);
146         token = strtok_r(NULL, ":", &saveptr);
147         if (token == NULL) {
148                 ERR("failed to parse size info");
149                 return;
150         }
151         size_info.ext_cache_size = atoll(token);
152         token = strtok_r(NULL, ":", &saveptr);
153         if (token == NULL) {
154                 ERR("failed to parse size info");
155                 return;
156         }
157         size_info.ext_app_size = atoll(token);
158
159         DBG("data: %lld, cache: %lld, app: %lld, ext_data: %lld, "
160                         "ext_cache: %lld, ext_app: %lld",
161                         size_info.data_size, size_info.cache_size,
162                         size_info.app_size, size_info.ext_data_size,
163                         size_info.ext_cache_size, size_info.ext_app_size);
164
165         if (strcmp(pkgid, PKG_SIZE_INFO_TOTAL) == 0) {
166                 callback = (pkgmgr_total_pkg_size_info_receive_cb)
167                         cb_info->size_info_cb;
168                 callback(cb_info->client, &size_info, cb_info->data);
169         } else {
170                 cb_info->size_info_cb(cb_info->client, pkgid, &size_info,
171                                 cb_info->data);
172         }
173 }
174
175 static void __convert_signal(char *event_type, char *event_status,
176                 char *appid, int progress, char **key, char **val)
177 {
178         *key = event_status;
179         if (strcmp(event_status, PKGMGR_INSTALLER_START_KEY_STR) == 0) {
180                 *val = event_type;
181         } else if (strcmp(event_status, PKGMGR_INSTALLER_OK_EVENT_STR) == 0 ||
182                         strcmp(event_status, PKGMGR_INSTALLER_FAIL_EVENT_STR) == 0) {
183                 *key = PKGMGR_INSTALLER_END_KEY_STR;
184                 *val = event_status;
185         } else if (strcmp(event_status, PKGMGR_INSTALLER_APPID_KEY_STR) == 0) {
186                 if (!appid) {
187                         ERR("appid is empty");
188                         return;
189                 }
190                 *val = appid;
191         } else if (strcmp(event_type, PKGMGR_INSTALLER_GET_SIZE_KEY_STR) == 0) {
192                 *key = event_type;
193                 *val = event_status;
194         }
195         // TODO: should handle cleardata / clearacache signal
196 }
197
198 static void __signal_handler(GDBusConnection *conn, const gchar *sender_name,
199                 const gchar *object_path, const gchar *interface_name,
200                 const gchar *signal_name, GVariant *parameters,
201                 gpointer user_data)
202 {
203         int progress = 0;
204         uid_t target_uid;
205         char buf[BUFMAX];
206         char *req_id;
207         char *pkg_type = NULL;
208         char *appid = NULL;
209         char *pkgid = NULL;
210         char *event_type = NULL;
211         char *event_status = NULL;
212         char *key = NULL;
213         char *val = NULL;
214         struct cb_info *cb_info = (struct cb_info *)user_data;
215         GVariantIter *iter = NULL;
216
217         g_variant_get(parameters, "(u&sa(sss)&s&si)", &target_uid, &req_id, &iter,
218                         &event_type, &event_status, &progress);
219         while (g_variant_iter_loop(iter, "(&s&s&s)", &pkgid, &appid, &pkg_type)) {
220                 if (cb_info->req_key) {
221                         if (strcmp(cb_info->req_key, req_id) != 0)
222                                 continue;
223                 }
224
225                 /* convert event_type and event_status into key-val pair */
226                 __convert_signal(event_type, event_status, appid,
227                                 progress, &key, &val);
228                 if (strcmp(event_status,
229                                         PKGMGR_INSTALLER_INSTALL_PERCENT_KEY_STR) == 0 ||
230                                 strcmp(event_status, PKGMGR_INSTALLER_ERROR_KEY_STR) == 0) {
231                         snprintf(buf, BUFMAX - 1, "%d", progress);
232                         val = buf;
233                 }
234                 /* TODO: progress should be set properly when installation has
235                  * completed or failed */
236
237                 if (cb_info->event_cb) {
238                         cb_info->event_cb(target_uid, cb_info->req_id,
239                                         pkg_type, pkgid, key, val, NULL, cb_info->data);
240                 } else if (cb_info->app_event_cb && strcmp(appid, "") != 0) {
241                         cb_info->app_event_cb(target_uid, cb_info->req_id,
242                                         pkg_type, pkgid, appid, key, val, NULL,
243                                         cb_info->data);
244                 } else if (cb_info->size_info_cb)
245                         __handle_size_info_callback(cb_info, pkgid, val);
246
247                 /* TODO: unsubscribe request callback */
248         }
249 }
250
251 static void __set_signal_list(int event, GList **signal_list)
252 {
253         int i;
254         if (event == PKGMGR_CLIENT_STATUS_ALL)
255                 return;
256
257         for (i = 0; map[i].signal_str != NULL; i++) {
258                 if (event & map[i].signal_type)
259                         *signal_list = g_list_append(*signal_list,
260                                         (char *)map[i].signal_str);
261         }
262 }
263
264 static int __subscribe_signal(struct pkgmgr_client_t *pc,
265                 struct cb_info *cb_info, const char *signal)
266 {
267         guint sid = g_dbus_connection_signal_subscribe(pc->conn, NULL,
268                         PKGMGR_INSTALLER_DBUS_INTERFACE, signal,
269                         PKGMGR_INSTALLER_DBUS_OBJECT_PATH, NULL,
270                         G_DBUS_SIGNAL_FLAGS_NONE, __signal_handler,
271                         (gpointer)cb_info, NULL);
272         if (!sid) {
273                 ERR("failed to subscribe singal");
274                 return PKGMGR_R_ERROR;
275         }
276         cb_info->sid_list = g_list_append(cb_info->sid_list,
277                         GUINT_TO_POINTER(sid));
278
279         return PKGMGR_R_OK;
280 }
281
282 int pkgmgr_client_connection_set_callback(struct pkgmgr_client_t *pc,
283                 struct cb_info *cb_info)
284 {
285         GList *signal_list = NULL;
286         GList *tmp_list = NULL;
287         int ret;
288         char *signal_type;
289         __set_signal_list(pc->status_type, &signal_list);
290
291         if (g_list_length(signal_list) == 0)
292                 return __subscribe_signal(pc, cb_info, NULL);
293
294         for (tmp_list = signal_list; tmp_list != NULL;
295                         tmp_list = g_list_next(tmp_list)) {
296                 signal_type = (char *)tmp_list->data;
297                 ret = __subscribe_signal(pc, cb_info, signal_type);
298                 if (ret != 0) {
299                         g_list_free(signal_list);
300                         return PKGMGR_R_ERROR;
301                 }
302         }
303
304         g_list_free(signal_list);
305         return PKGMGR_R_OK;
306 }
307
308 void pkgmgr_client_connection_unset_callback(struct pkgmgr_client_t *pc,
309                 struct cb_info *cb_info)
310 {
311         GList *tmp = NULL;
312         guint tmp_sid;
313         for (tmp = cb_info->sid_list; tmp != NULL; tmp = g_list_next(tmp)) {
314                 tmp_sid = GPOINTER_TO_UINT(tmp->data);
315                 g_dbus_connection_signal_unsubscribe(pc->conn, tmp_sid);
316         }
317 }
318
319 int pkgmgr_client_connection_send_request(struct pkgmgr_client_t *pc,
320                 const char *method, GVariant *params, GVariant **result)
321 {
322         GError *error = NULL;
323         GDBusProxy *proxy;
324         GVariant *r = NULL;
325         int retry_cnt = 0;
326         int ret = PKGMGR_R_ECOMM;
327
328         /* convert floating ref into normal ref */
329         g_variant_ref_sink(params);
330
331         do {
332                 proxy = g_dbus_proxy_new_sync(pc->conn,
333                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
334                                 NULL, PKGMGR_DBUS_SERVICE,
335                                 PKGMGR_DBUS_OBJECT_PATH,
336                                 PKGMGR_DBUS_INTERFACE, NULL, &error);
337                 if (proxy == NULL) {
338                         ERR("failed to get proxy object, sleep and retry[%s]",
339                                         error->message);
340                         g_error_free(error);
341                         error = NULL;
342                         usleep(CONNECTION_WAIT_USEC);
343                         retry_cnt++;
344                         continue;
345                 }
346
347                 r = g_dbus_proxy_call_sync(proxy, method, params,
348                                 G_DBUS_CALL_FLAGS_NONE,
349                                 CONNECTION_TIMEOUT_MSEC, NULL, &error);
350                 g_object_unref(proxy);
351                 if (error && error->code == G_DBUS_ERROR_ACCESS_DENIED) {
352                         ERR("failed to send request, privilege denied[%s]",
353                                         error->message);
354                         g_error_free(error);
355                         ret = PKGMGR_R_EPRIV;
356                         break;
357                 }
358                 if (r) {
359                         *result = r;
360                         ret = PKGMGR_R_OK;
361                         break;
362                 }
363
364                 ERR("failed to send request, sleep and retry[%s]",
365                                 error->message);
366                 g_error_free(error);
367                 error = NULL;
368                 usleep(CONNECTION_WAIT_USEC);
369                 retry_cnt++;
370         } while (retry_cnt <= CONNECTION_RETRY_MAX);
371
372         /* decrease ref count to 0 to free resource */
373         g_variant_unref(params);
374
375         return ret;
376 }