Use private dbus connection for synchronous calls.
[platform/core/system/libstorage.git] / src / storage-external-dbus.c
1 /*
2  * libstorage
3  *
4  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <vconf.h>
23 #include <stdbool.h>
24 #include <errno.h>
25 #include <gio/gio.h>
26 #include <assert.h>
27 #include <limits.h>
28 #include <glib.h>
29 #include <sys/statvfs.h>
30 #include <tzplatform_config.h>
31 #include <libsyscommon/libgdbus.h>
32 #include <libsyscommon/list.h>
33
34 #include "log.h"
35 #include "storage-external-dbus.h"
36
37 #define CHECK_STR(a) (a ? a : "")
38
39 #define STORAGE_EXT_GET_LIST               "GetDeviceList"
40 #define STORAGE_EXT_GET_STATVFS            "GetStatvfs"
41 #define STORAGE_EXT_GET_STORAGE_LEVEL      "GetStorageLevel"
42
43 #define STORAGE_EXT_DEVICE_CHANGED         "DeviceChanged"
44 #define STORAGE_EXT_DEVICE_ADDED           "DeviceAdded"
45 #define STORAGE_EXT_DEVICE_REMOVED         "DeviceRemoved"
46 #define STORAGE_EXT_DEVICE_BLOCKED         "DeviceBlocked"
47
48 #define DBUS_REPLY_TIMEOUT (-1)
49
50 #define GET_DBUS_CONN_OR_EXIT() \
51         ({ \
52                 dbus_handle_h dbus_handle = gdbus_get_connection(G_BUS_TYPE_SYSTEM, true); \
53                 if (dbus_handle == NULL) { \
54                         _E("Failed to get dbus connection"); \
55                         return -EIO; \
56                 } \
57                 dbus_handle; \
58         })
59
60
61 struct storage_ext_callback {
62         storage_ext_changed_cb func;
63         void *data;
64         guint block_id;
65 };
66
67 static GList *changed_list;
68
69 static void storage_ext_release_internal(storage_ext_device *dev)
70 {
71         if (!dev)
72                 return;
73         free(dev->devnode);
74         free(dev->syspath);
75         free(dev->fs_usage);
76         free(dev->fs_type);
77         free(dev->fs_version);
78         free(dev->fs_uuid);
79         free(dev->mount_point);
80 }
81
82 void storage_ext_release_device(storage_ext_device **dev)
83 {
84         if (!dev || !*dev)
85                 return;
86         storage_ext_release_internal(*dev);
87         free(*dev);
88         *dev = NULL;
89 }
90
91 void storage_ext_release_list(GList **list)
92 {
93         storage_ext_device *dev;
94         GList *elem;
95
96         if (*list == NULL)
97                 return;
98
99         SYS_G_LIST_FOREACH(*list, elem, dev) {
100                 storage_ext_release_internal(dev);
101                 free(dev);
102         }
103
104         g_list_free(*list);
105         *list = NULL;
106 }
107
108 int storage_ext_get_list(GList **list)
109 {
110         GVariant *reply;
111         GVariantIter *iter;
112         storage_ext_device *elem, info;
113         int ret, ret_dbus;
114
115         if (!list)
116                 return -EINVAL;
117
118         dbus_handle_h dbus_handle = GET_DBUS_CONN_OR_EXIT();
119
120         ret_dbus = gdbus_priv_call_sync_with_reply(dbus_handle,
121                         STORAGE_EXT_BUS_NAME,
122                         STORAGE_EXT_PATH_MANAGER,
123                         STORAGE_EXT_IFACE_MANAGER,
124                         STORAGE_EXT_GET_LIST,
125                         g_variant_new("(s)", "all"),
126                         &reply);
127
128         gdbus_free_connection(dbus_handle);
129
130         if (ret_dbus < 0) {
131                 _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
132                 return -EIO;
133         }
134
135         if (!g_variant_get_safe(reply, "(a(issssssisibii))", &iter)) {
136                 _E("Failed to get params from gvariant.");
137                 g_variant_unref(reply);
138                 return -EIO;
139         }
140
141         while (g_variant_iter_loop(iter, "(issssssisibii)",
142                                 &info.type, &info.devnode, &info.syspath,
143                                 &info.fs_usage, &info.fs_type,
144                                 &info.fs_version, &info.fs_uuid,
145                                 &info.readonly, &info.mount_point,
146                                 &info.state, &info.primary,
147                                 &info.flags, &info.storage_id)) {
148
149                 elem = (storage_ext_device *)malloc(sizeof(storage_ext_device));
150                 if (!elem) {
151                         _E("malloc() failed"); //LCOV_EXCL_LINE
152                         ret = -ENOMEM;
153                         goto out;
154                 }
155
156                 elem->type = info.type;
157                 elem->readonly = info.readonly;
158                 elem->state = info.state;
159                 elem->primary = info.primary;
160                 elem->devnode = strdup(CHECK_STR(info.devnode));
161                 elem->syspath = strdup(CHECK_STR(info.syspath));
162                 elem->fs_usage = strdup(CHECK_STR(info.fs_usage));
163                 elem->fs_type = strdup(CHECK_STR(info.fs_type));
164                 elem->fs_version = strdup(CHECK_STR(info.fs_version));
165                 elem->fs_uuid = strdup(CHECK_STR(info.fs_uuid));
166                 elem->mount_point = strdup(CHECK_STR(info.mount_point));
167                 elem->flags = info.flags;
168                 elem->storage_id = info.storage_id;
169
170                 SYS_G_LIST_APPEND(*list, elem);
171         }
172
173         ret = SYS_G_LIST_LENGTH(*list);
174
175 out:
176         if (ret < 0)
177                 storage_ext_release_list(list); //LCOV_EXCL_LINE System Error
178         g_variant_iter_free(iter);
179         g_variant_unref(reply);
180         return ret;
181 }
182
183 int storage_ext_get_statvfs(char *path, struct statvfs_32 *buf)
184 {
185         GVariant *reply;
186         int ret_dbus;
187         guint64 bsize, frsize, blocks, bfree, bavail, files, ffree, favail, fsid, flag, namemax;
188
189         assert(buf);
190
191         memset(buf, 0, sizeof(struct statvfs_32));
192
193         dbus_handle_h dbus_handle = GET_DBUS_CONN_OR_EXIT();
194
195         ret_dbus = gdbus_priv_call_sync_with_reply(dbus_handle,
196                         STORAGE_EXT_BUS_NAME,
197                         STORAGE_EXT_PATH_STORAGE,
198                         STORAGE_EXT_IFACE_STORAGE,
199                         STORAGE_EXT_GET_STATVFS,
200                         g_variant_new("(s)", path),
201                         &reply);
202
203         gdbus_free_connection(dbus_handle);
204
205         if (ret_dbus < 0) {
206                 _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
207                 return -EIO;
208         }
209
210         if (!g_variant_get_safe(reply, "(ttttttttttt)",
211                         &bsize, &frsize, &blocks,
212                         &bfree, &bavail, &files,
213                         &ffree, &favail, &fsid,
214                         &flag, &namemax)) {
215                 _E("Failed to get params from gvariant.");
216                 g_variant_unref(reply);
217                 return -EIO;
218         }
219 //      %llu bsize, frsize, blocks, bfree, bavail, files, ffree, favail, fsid, flag, namemax
220
221         buf->f_bsize  = (unsigned long)bsize;
222         buf->f_frsize = (unsigned long)frsize;
223         buf->f_blocks = (unsigned long)blocks;
224         buf->f_bfree  = (unsigned long)bfree;
225         buf->f_bavail = (unsigned long)bavail;
226         buf->f_files  = (unsigned long)files;
227         buf->f_ffree  = (unsigned long)ffree;
228         buf->f_favail = (unsigned long)favail;
229         buf->f_fsid = (unsigned long)fsid;
230         buf->f_flag = (unsigned long)flag;
231         buf->f_namemax = (unsigned long)namemax;
232
233 //      %lu buf->f_bsize, buf->f_frsize, buf->f_blocks, buf->f_bfree, buf->f_bavail, buf->f_files, buf->f_ffree, buf->f_favail, buf->f_fsid, buf->f_flag, buf->f_namemax
234         return 0;
235 }
236
237 int storage_ext_get_statvfs_size64(char *path, struct statvfs *buf)
238 {
239         GVariant *reply;
240         int ret_dbus;
241
242         assert(buf);
243
244         memset(buf, 0, sizeof(struct statvfs));
245
246         dbus_handle_h dbus_handle = GET_DBUS_CONN_OR_EXIT();
247
248         ret_dbus = gdbus_priv_call_sync_with_reply(dbus_handle,
249                         STORAGE_EXT_BUS_NAME,
250                         STORAGE_EXT_PATH_STORAGE,
251                         STORAGE_EXT_IFACE_STORAGE,
252                         STORAGE_EXT_GET_STATVFS,
253                         g_variant_new("(s)", path),
254                         &reply);
255
256         gdbus_free_connection(dbus_handle);
257
258         if (ret_dbus < 0) {
259                 _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
260                 return -EIO;
261         }
262
263         if (!g_variant_get_safe(reply, "(ttttttttttt)",
264                         &(buf->f_bsize), &(buf->f_frsize), &(buf->f_blocks),
265                         &(buf->f_bfree), &(buf->f_bavail), &(buf->f_files),
266                         &(buf->f_ffree), &(buf->f_favail), &(buf->f_fsid),
267                         &(buf->f_flag), &(buf->f_namemax))) {
268                 _E("Failed to get params from gvariant.");
269                 g_variant_unref(reply);
270                 return -EIO;
271         }
272
273 //      %lu buf->f_bsize, buf->f_frsize, buf->f_fsid, buf->f_flag, buf->f_namemax
274 //      %llu buf->f_blocks, buf->f_bfree, buf->f_bavail, buf->f_files, buf->f_ffree, buf->f_favail
275
276         return 0;
277 }
278
279 int storage_ext_get_storage_level(const char *path, char **level)
280 {
281         GVariant *reply;
282         int ret_dbus;
283         char *reply_val;
284         enum tzplatform_variable id;
285
286         if (!strcmp(path, tzplatform_getenv(TZ_SYS_USER)))
287                 id = TZ_SYS_USER;
288         else if (!strcmp(path, tzplatform_getenv(TZ_SYS_TMP)))
289                 id = TZ_SYS_TMP;
290         else if (!strcmp(path, tzplatform_getenv(TZ_SYS_OPT)))
291                 id = TZ_SYS_OPT;
292         else {
293                 _E("Invalid path");
294                 return -EINVAL;
295         }
296
297         dbus_handle_h dbus_handle = GET_DBUS_CONN_OR_EXIT();
298
299         ret_dbus = gdbus_priv_call_sync_with_reply(
300                         dbus_handle,
301                         STORAGE_EXT_BUS_NAME,
302                         STORAGE_EXT_PATH_STORAGE,
303                         STORAGE_EXT_IFACE_STORAGE,
304                         STORAGE_EXT_GET_STORAGE_LEVEL,
305                         g_variant_new("(i)", id),
306                         &reply);
307
308         gdbus_free_connection(dbus_handle);
309
310         if (ret_dbus < 0) {
311                 _E("Failed to get %d level", id);
312                 return -EIO;
313         }
314
315         if (!g_variant_get_safe(reply, "(s)", &reply_val)) {
316                 _E("Failed to get params from gvariant.");
317                 g_variant_unref(reply);
318                 return -EIO;
319         }
320
321         *level = strdup(reply_val);
322         g_free(reply_val);
323         g_variant_unref(reply);
324
325         if (*level == NULL)
326                 return -ENOMEM;
327
328         return 0;
329 }
330
331 //LCOV_EXCL_START Not called Callback
332 static void storage_ext_device_changed(GVariant *params, enum storage_ext_state state, gpointer user_data)
333 {
334         storage_ext_device *dev;
335         GList *elem;
336         struct storage_ext_callback *callback;
337         int ret_val;
338
339         if (!params)
340                 return;
341
342         dev = calloc(1, sizeof(storage_ext_device));
343         if (!dev)
344                 return;
345
346         if (!g_variant_get_safe(params, "(issssssisibii)",
347                         &dev->type,
348                         &dev->devnode,
349                         &dev->syspath,
350                         &dev->fs_usage,
351                         &dev->fs_type,
352                         &dev->fs_version,
353                         &dev->fs_uuid,
354                         &dev->readonly,
355                         &dev->mount_point,
356                         &dev->state,
357                         &dev->primary,
358                         &dev->flags,
359                         &dev->storage_id))
360                 return;
361
362         /* Callback is called when unmount is started(DeviceBlocked signal) */
363         if (state == STORAGE_EXT_CHANGED && dev->state == STORAGE_EXT_UNMOUNTED) {
364                 storage_ext_release_device(&dev);
365                 return;
366         }
367
368         SYS_G_LIST_FOREACH(changed_list, elem, callback) {
369                 if (!callback->func)
370                         continue;
371                 ret_val = callback->func(dev, state, callback->data);
372                 if (ret_val < 0)
373                         _E("Failed to call callback for devnode(%s, %d)", dev->devnode, ret_val);
374         }
375
376         storage_ext_release_device(&dev);
377 }
378 //LCOV_EXCL_STOP
379
380 //LCOV_EXCL_START Not called Callback
381 static void storage_ext_changed(GDBusConnection *conn,
382                 const gchar *sender,
383                 const gchar *path,
384                 const gchar *iface,
385                 const gchar *signal,
386                 GVariant *params,
387                 gpointer user_data)
388 {
389         size_t iface_len, signal_len;
390         enum storage_ext_state state;
391
392         if (!params || !sender || !path || !iface || !signal)
393                 return;
394
395         iface_len = strlen(iface) + 1;
396         signal_len = strlen(signal) + 1;
397
398         if (strncmp(iface, STORAGE_EXT_IFACE_MANAGER, iface_len))
399                 return;
400
401         if (!strncmp(signal, STORAGE_EXT_DEVICE_CHANGED, signal_len))
402                 state = STORAGE_EXT_CHANGED;
403
404         else if (!strncmp(signal, STORAGE_EXT_DEVICE_ADDED, signal_len))
405                 state = STORAGE_EXT_ADDED;
406
407         else if (!strncmp(signal, STORAGE_EXT_DEVICE_REMOVED, signal_len))
408                 state = STORAGE_EXT_REMOVED;
409
410         else if (!strncmp(signal, STORAGE_EXT_DEVICE_BLOCKED, signal_len))
411                 state = STORAGE_EXT_BLOCKED;
412
413         else
414                 return;
415
416         storage_ext_device_changed(params, state, user_data);
417 }
418 //LCOV_EXCL_STOP
419
420 int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
421 {
422         guint block_id = 0;
423         struct storage_ext_callback *callback;
424         GList *elem;
425
426         if (!func)
427                 return -EINVAL;
428
429         SYS_G_LIST_FOREACH(changed_list, elem, callback) {
430                 if (callback->func != func)
431                         continue;
432                 if (callback->block_id == 0)
433                         continue;
434
435                 return -EEXIST;
436         }
437
438         callback = (struct storage_ext_callback *)malloc(sizeof(struct storage_ext_callback));
439         if (!callback) {
440                 //LCOV_EXCL_START System Error
441                 _E("malloc() failed");
442                 return -ENOMEM;
443                 //LCOV_EXCL_STOP
444         }
445
446         block_id = gdbus_signal_subscribe(NULL, NULL,
447                         STORAGE_EXT_IFACE_MANAGER,
448                         NULL,
449                         storage_ext_changed,
450                         NULL,
451                         NULL);
452         if (block_id == 0) {
453                 //LCOV_EXCL_START System Error
454                 free(callback);
455                 _E("Failed to subscrive bus signal");
456                 return -EPERM;
457                 //LCOV_EXCL_STOP
458         }
459
460         callback->func = func;
461         callback->data = data;
462         callback->block_id = block_id;
463
464         SYS_G_LIST_APPEND(changed_list, callback);
465
466         return 0;
467 }
468
469 void storage_ext_unregister_device_change(storage_ext_changed_cb func)
470 {
471         struct storage_ext_callback *callback;
472         GList *elem;
473         GList *elem_n;
474
475         if (!func)
476                 return;
477
478         SYS_G_LIST_FOREACH_SAFE(changed_list, elem, elem_n, callback) {
479                 if (callback->func != func)
480                         continue;
481                 if (callback->block_id > 0)
482                         gdbus_signal_unsubscribe(NULL, callback->block_id);
483
484                 SYS_G_LIST_REMOVE(changed_list, callback);
485                 free(callback);
486         }
487 }
488
489 int storage_ext_get_device_info(int storage_id, storage_ext_device *info)
490 {
491         GVariant *reply;
492         int ret_dbus;
493
494         dbus_handle_h dbus_handle = GET_DBUS_CONN_OR_EXIT();
495
496         ret_dbus = gdbus_priv_call_sync_with_reply(dbus_handle,
497                         STORAGE_EXT_BUS_NAME,
498                         STORAGE_EXT_PATH_MANAGER,
499                         STORAGE_EXT_IFACE_MANAGER,
500                         "GetDeviceInfo",
501                         g_variant_new("(i)", storage_id),
502                         &reply);
503
504         gdbus_free_connection(dbus_handle);
505
506         if (ret_dbus < 0) {
507                 _E("There is no storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
508                 return -ENODEV;
509         }
510
511         if (!g_variant_get_safe(reply, "(issssssisibii)",
512                                 &info->type, &info->devnode, &info->syspath,
513                                 &info->fs_usage, &info->fs_type,
514                                 &info->fs_version, &info->fs_uuid,
515                                 &info->readonly, &info->mount_point,
516                                 &info->state, &info->primary,
517                                 &info->flags, &info->storage_id)) {
518                 _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
519                 ret_dbus = -ENODEV;
520                 goto out;
521         }
522
523         if (info->storage_id < 0) {
524                 _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
525                 ret_dbus = -ENODEV;
526                 goto out;
527         }
528
529 out:
530         g_variant_unref(reply);
531         return 0;
532 }