731296a7ffe8b746b074b56451699d082578afd7
[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 <glib.h>
27 #include <assert.h>
28 #include <limits.h>
29 #include <sys/statvfs.h>
30 #include <tzplatform_config.h>
31
32 #include "log.h"
33 #include "storage-external-dbus.h"
34
35 #define CHECK_STR(a) (a ? a : "")
36
37 #define STORAGE_EXT_GET_LIST               "GetDeviceList"
38 #define STORAGE_EXT_GET_STATVFS            "GetStatvfs"
39 #define STORAGE_EXT_GET_STORAGE_LEVEL      "GetStorageLevel"
40
41 #define STORAGE_EXT_DEVICE_CHANGED         "DeviceChanged"
42 #define STORAGE_EXT_DEVICE_ADDED           "DeviceAdded"
43 #define STORAGE_EXT_DEVICE_REMOVED         "DeviceRemoved"
44 #define STORAGE_EXT_DEVICE_BLOCKED         "DeviceBlocked"
45
46 #define DBUS_REPLY_TIMEOUT (-1)
47
48 struct storage_ext_callback {
49         storage_ext_changed_cb func;
50         void *data;
51         guint block_id;
52 };
53
54 typedef struct {
55         dbus_pending_cb func;
56         void *data;
57 } pending_call_data;
58
59 static dd_list *changed_list;
60
61 static void storage_ext_release_internal(storage_ext_device *dev)
62 {
63         if (!dev)
64                 return;
65         free(dev->devnode);
66         free(dev->syspath);
67         free(dev->fs_usage);
68         free(dev->fs_type);
69         free(dev->fs_version);
70         free(dev->fs_uuid);
71         free(dev->mount_point);
72 }
73
74 void storage_ext_release_device(storage_ext_device **dev)
75 {
76         if (!dev || !*dev)
77                 return;
78         storage_ext_release_internal(*dev);
79         free(*dev);
80         *dev = NULL;
81 }
82
83 void storage_ext_release_list(dd_list **list)
84 {
85         storage_ext_device *dev;
86         dd_list *elem;
87
88         if (*list == NULL)
89                 return;
90
91         DD_LIST_FOREACH(*list, elem, dev) {
92                 storage_ext_release_internal(dev);
93                 free(dev);
94         }
95
96         g_list_free(*list);
97         *list = NULL;
98 }
99
100 static GDBusConnection *get_dbus_connection(void)
101 {
102         GError *err = NULL;
103         static GDBusConnection *conn;
104
105         if (conn)
106                 return conn;
107
108 #if !GLIB_CHECK_VERSION(2, 35, 0)
109         g_type_init();
110 #endif
111
112         conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
113         if (!conn) {
114 //LCOV_EXCL_START System Error
115                 if (err) {
116                         _E("fail to get dbus connection : %s", err->message);
117                         g_clear_error(&err);
118                 } else
119                         _E("fail to get dbus connection");
120                 return NULL;
121 //LCOV_EXCL_STOP
122         }
123         return conn;
124 }
125
126 static void _cb_pending(GDBusConnection *conn,
127                         GAsyncResult *res,
128                         gpointer user_data)
129 {
130         GVariant *reply = NULL;
131         GError *err = NULL;
132         pending_call_data *data = (pending_call_data *)user_data;
133
134         reply = g_dbus_connection_call_finish(conn, res, &err);
135         if (!reply || err) {
136                 if (!err)
137                         g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED,
138                                 "Error during g_dbus_connection_call");
139
140                 if (data && data->func)
141                         data->func(NULL, data->data, err);
142                 goto out;
143         }
144
145         if (data && data->func)
146                 data->func(reply, data->data, err);
147 out:
148         if (err)
149                 g_error_free(err);
150         if (data)
151                 free(data);
152 }
153
154 int dbus_method_async_with_reply_var(const char *dest, const char *path,
155                 const char *iface, const char *method, GVariant *param,
156                 dbus_pending_cb cb, int timeout, void *data)
157 {
158         GDBusConnection *conn;
159         pending_call_data *pdata = NULL;
160         int ret = 0;
161
162         if (!dest || !path || !iface || !method)
163                 return -EINVAL;
164
165         if (timeout < -1) {
166                 _E("wrong timeout %d", timeout);
167                 return -EINVAL;
168         }
169
170         conn = get_dbus_connection();
171         if (!conn) {
172                 _E("fail to get dbus connection"); //LCOV_EXCL_LINE
173                 return -1;
174         }
175
176         if (cb) {
177                 pdata = (pending_call_data*)malloc(sizeof(pending_call_data));
178                 if (!pdata) {
179                         ret = -ENOMEM;
180                         goto err;
181                 }
182
183                 pdata->func = cb;
184                 pdata->data = data;
185         }
186
187         g_dbus_connection_call(conn, dest, path, iface, method,
188                         param, NULL, G_DBUS_CALL_FLAGS_NONE, timeout, NULL,
189                         (GAsyncReadyCallback)_cb_pending,
190                         pdata);
191
192         return ret;
193 err:
194         if (param)
195                 g_variant_unref(param);
196         return ret;
197 }
198
199 GVariant *dbus_method_call_sync(const gchar *dest, const gchar *path,
200                                 const gchar *iface, const gchar *method, GVariant *param)
201 {
202         GDBusConnection *conn;
203         GError *err = NULL;
204         GVariant *ret;
205
206         if (!dest || !path || !iface || !method)
207                 return NULL;
208
209         conn = get_dbus_connection();
210         if (!conn) {
211                 _E("fail to get dbus connection"); //LCOV_EXCL_LINE
212                 return NULL;
213         }
214
215         ret = g_dbus_connection_call_sync(conn,
216                         dest, path, iface, method,
217                         param, NULL, G_DBUS_CALL_FLAGS_NONE,
218                         -1, NULL, &err);
219         if (!ret) {
220 //LCOV_EXCL_START System Error
221                 if (err) {
222                         _E("dbus method sync call failed(%s)", err->message);
223                         g_clear_error(&err);
224                 } else
225                         _E("g_dbus_connection_call_sync() failed");
226                 return NULL;
227 //LCOV_EXCL_STOP
228         }
229
230         return ret;
231 }
232
233 int storage_ext_get_list(dd_list **list)
234 {
235         GVariant *result;
236         GVariantIter *iter;
237         storage_ext_device *elem, info;
238         int ret;
239
240         if (!list)
241                 return -EINVAL;
242
243         result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
244                         STORAGE_EXT_PATH_MANAGER,
245                         STORAGE_EXT_IFACE_MANAGER,
246                         STORAGE_EXT_GET_LIST,
247                         g_variant_new("(s)", "all"));
248         if (!result) {
249                 _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
250                 return -EIO;
251         }
252
253         g_variant_get(result, "(a(issssssisibii))", &iter);
254
255         while (g_variant_iter_loop(iter, "(issssssisibii)",
256                                 &info.type, &info.devnode, &info.syspath,
257                                 &info.fs_usage, &info.fs_type,
258                                 &info.fs_version, &info.fs_uuid,
259                                 &info.readonly, &info.mount_point,
260                                 &info.state, &info.primary,
261                                 &info.flags, &info.storage_id)) {
262
263                 elem = (storage_ext_device *)malloc(sizeof(storage_ext_device));
264                 if (!elem) {
265                         _E("malloc() failed"); //LCOV_EXCL_LINE
266                         ret = -ENOMEM;
267                         goto out;
268                 }
269
270                 elem->type = info.type;
271                 elem->readonly = info.readonly;
272                 elem->state = info.state;
273                 elem->primary = info.primary;
274                 elem->devnode = strdup(CHECK_STR(info.devnode));
275                 elem->syspath = strdup(CHECK_STR(info.syspath));
276                 elem->fs_usage = strdup(CHECK_STR(info.fs_usage));
277                 elem->fs_type = strdup(CHECK_STR(info.fs_type));
278                 elem->fs_version = strdup(CHECK_STR(info.fs_version));
279                 elem->fs_uuid = strdup(CHECK_STR(info.fs_uuid));
280                 elem->mount_point = strdup(CHECK_STR(info.mount_point));
281                 elem->flags = info.flags;
282                 elem->storage_id = info.storage_id;
283
284                 DD_LIST_APPEND(*list, elem);
285         }
286
287         ret = g_list_length(*list);
288
289 out:
290         if (ret < 0)
291                 storage_ext_release_list(list); //LCOV_EXCL_LINE System Error
292         g_variant_iter_free(iter);
293         g_variant_unref(result);
294         return ret;
295 }
296
297 int storage_ext_get_statvfs(char *path, struct statvfs_32 *buf)
298 {
299         GVariant *result;
300         guint64 bsize, frsize, blocks, bfree, bavail, files, ffree, favail, fsid, flag, namemax;
301
302         assert(buf);
303
304         memset(buf, 0, sizeof(struct statvfs_32));
305
306         result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
307                         STORAGE_EXT_PATH_STORAGE,
308                         STORAGE_EXT_IFACE_STORAGE,
309                         STORAGE_EXT_GET_STATVFS,
310                         g_variant_new("(s)", path));
311         if (!result) {
312                 _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
313                 return -EIO;
314         }
315
316         g_variant_get(result, "(ttttttttttt)",
317                         &bsize, &frsize, &blocks,
318                         &bfree, &bavail, &files,
319                         &ffree, &favail, &fsid,
320                         &flag, &namemax);
321 //      %llu bsize, frsize, blocks, bfree, bavail, files, ffree, favail, fsid, flag, namemax
322
323         buf->f_bsize  = (unsigned long)bsize;
324         buf->f_frsize = (unsigned long)frsize;
325         buf->f_blocks = (unsigned long)blocks;
326         buf->f_bfree  = (unsigned long)bfree;
327         buf->f_bavail = (unsigned long)bavail;
328         buf->f_files  = (unsigned long)files;
329         buf->f_ffree  = (unsigned long)ffree;
330         buf->f_favail = (unsigned long)favail;
331         buf->f_fsid = (unsigned long)fsid;
332         buf->f_flag = (unsigned long)flag;
333         buf->f_namemax = (unsigned long)namemax;
334
335 //      %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
336         return 0;
337 }
338
339 int storage_ext_get_statvfs_size64(char *path, struct statvfs *buf)
340 {
341         GVariant *result;
342
343         assert(buf);
344
345         memset(buf, 0, sizeof(struct statvfs));
346
347         result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
348                         STORAGE_EXT_PATH_STORAGE,
349                         STORAGE_EXT_IFACE_STORAGE,
350                         STORAGE_EXT_GET_STATVFS,
351                         g_variant_new("(s)", path));
352         if (!result) {
353                 _E("Failed to get storage_ext device info"); //LCOV_EXCL_LINE
354                 return -EIO;
355         }
356
357         g_variant_get(result, "(ttttttttttt)",
358                         &(buf->f_bsize), &(buf->f_frsize), &(buf->f_blocks),
359                         &(buf->f_bfree), &(buf->f_bavail), &(buf->f_files),
360                         &(buf->f_ffree), &(buf->f_favail), &(buf->f_fsid),
361                         &(buf->f_flag), &(buf->f_namemax));
362
363 //      %lu buf->f_bsize, buf->f_frsize, buf->f_fsid, buf->f_flag, buf->f_namemax
364 //      %llu buf->f_blocks, buf->f_bfree, buf->f_bavail, buf->f_files, buf->f_ffree, buf->f_favail
365
366         return 0;
367 }
368
369 int storage_ext_get_storage_level(const char *path, char **level)
370 {
371         GVariant *result;
372         char *tmp;
373         enum tzplatform_variable id;
374
375         if (!strcmp(path, tzplatform_getenv(TZ_SYS_USER)))
376                 id = TZ_SYS_USER;
377         else if (!strcmp(path, tzplatform_getenv(TZ_SYS_TMP)))
378                 id = TZ_SYS_TMP;
379         else if (!strcmp(path, tzplatform_getenv(TZ_SYS_OPT)))
380                 id = TZ_SYS_OPT;
381         else {
382                 _E("Invalid path");
383                 return -EINVAL;
384         }
385
386         result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
387                         STORAGE_EXT_PATH_STORAGE,
388                         STORAGE_EXT_IFACE_STORAGE,
389                         STORAGE_EXT_GET_STORAGE_LEVEL,
390                         g_variant_new("(i)", id));
391         if (!result) {
392                 _E("Failed to get %d level", id);
393                 return -EIO;
394         }
395
396         g_variant_get(result, "(s)", &tmp);
397         *level = strdup(tmp);
398         g_free(tmp);
399
400         if (*level == NULL)
401                 return -ENOMEM;
402
403         return 0;
404 }
405
406 //LCOV_EXCL_START Not called Callback
407 static void storage_ext_device_changed(GVariant *params, enum storage_ext_state state, gpointer user_data)
408 {
409         storage_ext_device *dev;
410         dd_list *elem;
411         struct storage_ext_callback *callback;
412         int ret;
413
414         if (!params)
415                 return;
416
417         dev = calloc(1, sizeof(storage_ext_device));
418         if (!dev)
419                 return;
420
421         g_variant_get(params, "(issssssisibii)",
422                         &dev->type,
423                         &dev->devnode,
424                         &dev->syspath,
425                         &dev->fs_usage,
426                         &dev->fs_type,
427                         &dev->fs_version,
428                         &dev->fs_uuid,
429                         &dev->readonly,
430                         &dev->mount_point,
431                         &dev->state,
432                         &dev->primary,
433                         &dev->flags,
434                         &dev->storage_id);
435
436         /* Callback is called when unmount is started(DeviceBlocked signal) */
437         if (state == STORAGE_EXT_CHANGED && dev->state == STORAGE_EXT_UNMOUNTED) {
438                 storage_ext_release_device(&dev);
439                 return;
440         }
441
442         DD_LIST_FOREACH(changed_list, elem, callback) {
443                 if (!callback->func)
444                         continue;
445                 ret = callback->func(dev, state, callback->data);
446                 if (ret < 0)
447                         _E("Failed to call callback for devnode(%s, %d)", dev->devnode, ret);
448         }
449
450         storage_ext_release_device(&dev);
451 }
452 //LCOV_EXCL_STOP
453
454 //LCOV_EXCL_START Not called Callback
455 static void storage_ext_changed(GDBusConnection *conn,
456                 const gchar *sender,
457                 const gchar *path,
458                 const gchar *iface,
459                 const gchar *signal,
460                 GVariant *params,
461                 gpointer user_data)
462 {
463         size_t iface_len, signal_len;
464         enum storage_ext_state state;
465
466         if (!params || !sender || !path || !iface || !signal)
467                 return;
468
469         iface_len = strlen(iface) + 1;
470         signal_len = strlen(signal) + 1;
471
472         if (strncmp(iface, STORAGE_EXT_IFACE_MANAGER, iface_len))
473                 return;
474
475         if (!strncmp(signal, STORAGE_EXT_DEVICE_CHANGED, signal_len))
476                 state = STORAGE_EXT_CHANGED;
477
478         else if (!strncmp(signal, STORAGE_EXT_DEVICE_ADDED, signal_len))
479                 state = STORAGE_EXT_ADDED;
480
481         else if (!strncmp(signal, STORAGE_EXT_DEVICE_REMOVED, signal_len))
482                 state = STORAGE_EXT_REMOVED;
483
484         else if (!strncmp(signal, STORAGE_EXT_DEVICE_BLOCKED, signal_len))
485                 state = STORAGE_EXT_BLOCKED;
486
487         else
488                 return;
489
490         storage_ext_device_changed(params, state, user_data);
491 }
492 //LCOV_EXCL_STOP
493
494 int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
495 {
496         GDBusConnection *conn;
497         guint block_id = 0;
498         struct storage_ext_callback *callback;
499         dd_list *elem;
500
501         if (!func)
502                 return -EINVAL;
503
504         DD_LIST_FOREACH(changed_list, elem, callback) {
505                 if (callback->func != func)
506                         continue;
507                 if (callback->block_id == 0)
508                         continue;
509
510                 return -EEXIST;
511         }
512
513         callback = (struct storage_ext_callback *)malloc(sizeof(struct storage_ext_callback));
514         if (!callback) {
515                 //LCOV_EXCL_START System Error
516                 _E("malloc() failed");
517                 return -ENOMEM;
518                 //LCOV_EXCL_STOP
519         }
520
521         conn = get_dbus_connection();
522         if (!conn) {
523                 //LCOV_EXCL_START System Error
524                 free(callback);
525                 _E("Failed to get dbus connection");
526                 return -EPERM;
527                 //LCOV_EXCL_STOP
528         }
529
530         block_id = g_dbus_connection_signal_subscribe(conn,
531                         NULL,
532                         STORAGE_EXT_IFACE_MANAGER,
533                         NULL,
534                         NULL,
535                         NULL,
536                         G_DBUS_SIGNAL_FLAGS_NONE,
537                         storage_ext_changed,
538                         NULL,
539                         NULL);
540         if (block_id == 0) {
541                 //LCOV_EXCL_START System Error
542                 free(callback);
543                 _E("Failed to subscrive bus signal");
544                 return -EPERM;
545                 //LCOV_EXCL_STOP
546         }
547
548         callback->func = func;
549         callback->data = data;
550         callback->block_id = block_id;
551
552         DD_LIST_APPEND(changed_list, callback);
553
554         return 0;
555 }
556
557 void storage_ext_unregister_device_change(storage_ext_changed_cb func)
558 {
559         GDBusConnection *conn;
560         struct storage_ext_callback *callback;
561         dd_list *elem;
562         dd_list *elem_n;
563
564         if (!func)
565                 return;
566
567         conn = get_dbus_connection();
568         if (!conn) {
569 //LCOV_EXCL_START System Error
570                 _E("fail to get dbus connection");
571                 return;
572 //LCOV_EXCL_STOP
573         }
574
575         DD_LIST_FOREACH_SAFE(changed_list, elem, elem_n, callback) {
576                 if (callback->func != func)
577                         continue;
578                 if (callback->block_id > 0)
579                         g_dbus_connection_signal_unsubscribe(conn, callback->block_id);
580
581                 DD_LIST_REMOVE(changed_list, callback);
582                 free(callback);
583         }
584 }
585
586 int storage_ext_get_device_info(int storage_id, storage_ext_device *info)
587 {
588         GVariant *result;
589
590         result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
591                         STORAGE_EXT_PATH_MANAGER,
592                         STORAGE_EXT_IFACE_MANAGER,
593                         "GetDeviceInfo",
594                         g_variant_new("(i)", storage_id));
595         if (!result) {
596                 _E("There is no storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
597                 return -ENODEV;
598         }
599
600         if (g_variant_check_format_string(result, "(issssssisibii)", true)) {
601                 g_variant_get(result, "(issssssisibii)",
602                                 &info->type, &info->devnode, &info->syspath,
603                                 &info->fs_usage, &info->fs_type,
604                                 &info->fs_version, &info->fs_uuid,
605                                 &info->readonly, &info->mount_point,
606                                 &info->state, &info->primary,
607                                 &info->flags, &info->storage_id);
608         } else {
609                 _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
610                 return -ENODEV;
611         }
612
613         if (info->storage_id < 0) {
614                 _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
615                 return -ENODEV;
616         }
617
618         g_variant_unref(result);
619
620         return 0;
621 }