Code refactoring
[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_APP_ENABLE_EVENT_STR,
96                 PKGMGR_CLIENT_STATUS_ENABLE_APP},
97         {PKGMGR_INSTALLER_APP_DISABLE_EVENT_STR,
98                 PKGMGR_CLIENT_STATUS_DISABLE_APP},
99         {PKGMGR_INSTALLER_APP_ENABLE_SPLASH_SCREEN_EVENT_STR,
100                 PKGMGR_CLIENT_STATUS_ENABLE_APP_SPLASH_SCREEN},
101         {PKGMGR_INSTALLER_APP_DISABLE_SPLASH_SCREEN_EVENT_STR,
102                 PKGMGR_CLIENT_STATUS_DISABLE_APP_SPLASH_SCREEN},
103         {NULL, -1}
104 };
105
106 static int __get_signal_type(const char *name)
107 {
108         int i;
109
110         if (name == NULL)
111                 return -1;
112
113         for (i = 0; map[i].signal_str != NULL; i++) {
114                 if (strcmp(map[i].signal_str, name) == 0)
115                         return map[i].signal_type;
116         }
117
118         return -1;
119 }
120
121 static void __handle_size_info_callback(struct cb_info *cb_info,
122                 const char *pkgid, const char *val)
123 {
124         pkg_size_info_t size_info;
125         char buf[BUFMAX];
126         char *saveptr;
127         char *token;
128         pkgmgr_total_pkg_size_info_receive_cb callback;
129
130         snprintf(buf, sizeof(buf), "%s", val);
131
132         DBG("%s, %s", pkgid, val);
133
134         token = strtok_r(buf, ":", &saveptr);
135         if (token == NULL) {
136                 ERR("failed to parse size info");
137                 return;
138         }
139         size_info.data_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.cache_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.app_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_data_size = atoll(token);
158         token = strtok_r(NULL, ":", &saveptr);
159         if (token == NULL) {
160                 ERR("failed to parse size info");
161                 return;
162         }
163         size_info.ext_cache_size = atoll(token);
164         token = strtok_r(NULL, ":", &saveptr);
165         if (token == NULL) {
166                 ERR("failed to parse size info");
167                 return;
168         }
169         size_info.ext_app_size = atoll(token);
170
171         DBG("data: %lld, cache: %lld, app: %lld, ext_data: %lld, "
172                         "ext_cache: %lld, ext_app: %lld",
173                         size_info.data_size, size_info.cache_size,
174                         size_info.app_size, size_info.ext_data_size,
175                         size_info.ext_cache_size, size_info.ext_app_size);
176
177         if (strcmp(pkgid, PKG_SIZE_INFO_TOTAL) == 0) {
178                 callback = (pkgmgr_total_pkg_size_info_receive_cb)
179                         cb_info->size_info_cb;
180                 callback(cb_info->client, &size_info, cb_info->data);
181         } else {
182                 cb_info->size_info_cb(cb_info->client, pkgid, &size_info,
183                                 cb_info->data);
184         }
185 }
186
187 static void __signal_handler(GDBusConnection *conn, const gchar *sender_name,
188                 const gchar *object_path, const gchar *interface_name,
189                 const gchar *signal_name, GVariant *parameters,
190                 gpointer user_data)
191 {
192         uid_t target_uid;
193         char *req_id;
194         char *pkg_type = NULL;
195         char *pkgid = NULL;
196         char *key = NULL;
197         char *val = NULL;
198         char *appid = NULL;
199         int signal_type;
200         struct cb_info *cb_info = (struct cb_info *)user_data;
201
202         g_variant_get(parameters, "(u&s&s&s&s&s&s)",
203                         &target_uid, &req_id, &pkg_type,
204                         &pkgid, &appid, &key, &val);
205
206         /* in case of request, check reqkey */
207         if (cb_info->req_key) {
208                 if (strcmp(cb_info->req_key, req_id) != 0)
209                         return;
210         } else {
211                 signal_type = __get_signal_type(signal_name);
212                 if (signal_type < 0 || !(cb_info->status_type & signal_type))
213                         return;
214         }
215
216         /* each cb_data can only has one callback */
217         if (cb_info->event_cb)
218                 cb_info->event_cb(target_uid, cb_info->req_id,
219                                 pkg_type, pkgid, key, val, NULL, cb_info->data);
220         else if (cb_info->app_event_cb)
221                 cb_info->app_event_cb(target_uid, cb_info->req_id,
222                                 pkg_type, pkgid, appid, key, val, NULL,
223                                 cb_info->data);
224         else if (cb_info->size_info_cb)
225                 __handle_size_info_callback(cb_info, pkgid, val);
226
227         /* for request, unsubscribe if end signal received */
228         if (cb_info->req_key &&
229                         (strcmp(key, "end") == 0 || strcmp(val, "end") == 0)) {
230                 g_dbus_connection_signal_unsubscribe(cb_info->client->conn,
231                                 cb_info->sid);
232                 cb_info->client->cb_info_list = g_list_remove(
233                                 cb_info->client->cb_info_list,
234                                 cb_info);
235                 free(cb_info->req_key);
236                 free(cb_info);
237         }
238 }
239
240 int pkgmgr_client_connection_set_callback(struct pkgmgr_client_t *pc,
241                 struct cb_info *cb_info)
242 {
243         cb_info->sid = g_dbus_connection_signal_subscribe(pc->conn, NULL,
244                         PKGMGR_INSTALLER_DBUS_INTERFACE, NULL,
245                         PKGMGR_INSTALLER_DBUS_OBJECT_PATH, NULL,
246                         G_DBUS_SIGNAL_FLAGS_NONE, __signal_handler,
247                         (gpointer)cb_info, NULL);
248         if (!cb_info->sid) {
249                 ERR("failed to subscribe singal");
250                 return PKGMGR_R_ERROR;
251         }
252
253         return PKGMGR_R_OK;
254 }
255
256 void pkgmgr_client_connection_unset_callback(struct pkgmgr_client_t *pc,
257                 struct cb_info *cb_info)
258 {
259         g_dbus_connection_signal_unsubscribe(pc->conn, cb_info->sid);
260 }
261
262 int pkgmgr_client_connection_send_request(struct pkgmgr_client_t *pc,
263                 const char *method, GVariant *params, GVariant **result)
264 {
265         GError *error = NULL;
266         GDBusProxy *proxy;
267         GVariant *r = NULL;
268         int retry_cnt = 0;
269         int ret = PKGMGR_R_ECOMM;
270
271         /* convert floating ref into normal ref */
272         g_variant_ref_sink(params);
273
274         do {
275                 proxy = g_dbus_proxy_new_sync(pc->conn, G_DBUS_PROXY_FLAGS_NONE,
276                                 NULL, PKGMGR_DBUS_SERVICE,
277                                 PKGMGR_DBUS_OBJECT_PATH,
278                                 PKGMGR_DBUS_INTERFACE, NULL, &error);
279                 if (proxy == NULL) {
280                         ERR("failed to get proxy object, sleep and retry[%s]",
281                                         error->message);
282                         g_error_free(error);
283                         error = NULL;
284                         usleep(CONNECTION_WAIT_USEC);
285                         retry_cnt++;
286                         continue;
287                 }
288
289                 r = g_dbus_proxy_call_sync(proxy, method, params,
290                                 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
291                 g_object_unref(proxy);
292                 if (error && error->code == G_DBUS_ERROR_ACCESS_DENIED) {
293                         ERR("failed to send request, privilege denied[%s]",
294                                         error->message);
295                         g_error_free(error);
296                         ret = PKGMGR_R_EPRIV;
297                         break;
298                 }
299                 if (r) {
300                         *result = r;
301                         ret = PKGMGR_R_OK;
302                         break;
303                 }
304
305                 ERR("failed to send request, sleep and retry[%s]",
306                                 error->message);
307                 g_error_free(error);
308                 error = NULL;
309                 usleep(CONNECTION_WAIT_USEC);
310                 retry_cnt++;
311         } while (retry_cnt <= CONNECTION_RETRY_MAX);
312
313         /* decrease ref count to 0 to free resource */
314         g_variant_unref(params);
315
316         return ret;
317 }