Add internal apis to mount/unmount/format primary sdcard
[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         if (*level == NULL)
399                 return -ENOMEM;
400
401         return 0;
402 }
403
404 //LCOV_EXCL_START Not called Callback
405 static void storage_ext_device_changed(GVariant *params, enum storage_ext_state state, gpointer user_data)
406 {
407         storage_ext_device *dev;
408         dd_list *elem;
409         struct storage_ext_callback *callback;
410         int ret;
411
412         if (!params)
413                 return;
414
415         dev = calloc(1, sizeof(storage_ext_device));
416         if (!dev)
417                 return;
418
419         g_variant_get(params, "(issssssisibii)",
420                         &dev->type,
421                         &dev->devnode,
422                         &dev->syspath,
423                         &dev->fs_usage,
424                         &dev->fs_type,
425                         &dev->fs_version,
426                         &dev->fs_uuid,
427                         &dev->readonly,
428                         &dev->mount_point,
429                         &dev->state,
430                         &dev->primary,
431                         &dev->flags,
432                         &dev->storage_id);
433
434         /* Callback is called when unmount is started(DeviceBlocked signal) */
435         if (state == STORAGE_EXT_CHANGED && dev->state == STORAGE_EXT_UNMOUNTED) {
436                 storage_ext_release_device(&dev);
437                 return;
438         }
439
440         DD_LIST_FOREACH(changed_list, elem, callback) {
441                 if (!callback->func)
442                         continue;
443                 ret = callback->func(dev, state, callback->data);
444                 if (ret < 0)
445                         _E("Failed to call callback for devnode(%s, %d)", dev->devnode, ret);
446         }
447
448         storage_ext_release_device(&dev);
449 }
450 //LCOV_EXCL_STOP
451
452 //LCOV_EXCL_START Not called Callback
453 static void storage_ext_changed(GDBusConnection *conn,
454                 const gchar *sender,
455                 const gchar *path,
456                 const gchar *iface,
457                 const gchar *signal,
458                 GVariant *params,
459                 gpointer user_data)
460 {
461         size_t iface_len, signal_len;
462         enum storage_ext_state state;
463
464         if (!params || !sender || !path || !iface || !signal)
465                 return;
466
467         iface_len = strlen(iface) + 1;
468         signal_len = strlen(signal) + 1;
469
470         if (strncmp(iface, STORAGE_EXT_IFACE_MANAGER, iface_len))
471                 return;
472
473         if (!strncmp(signal, STORAGE_EXT_DEVICE_CHANGED, signal_len))
474                 state = STORAGE_EXT_CHANGED;
475
476         else if (!strncmp(signal, STORAGE_EXT_DEVICE_ADDED, signal_len))
477                 state = STORAGE_EXT_ADDED;
478
479         else if (!strncmp(signal, STORAGE_EXT_DEVICE_REMOVED, signal_len))
480                 state = STORAGE_EXT_REMOVED;
481
482         else if (!strncmp(signal, STORAGE_EXT_DEVICE_BLOCKED, signal_len))
483                 state = STORAGE_EXT_BLOCKED;
484
485         else
486                 return;
487
488         storage_ext_device_changed(params, state, user_data);
489 }
490 //LCOV_EXCL_STOP
491
492 int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
493 {
494         GDBusConnection *conn;
495         guint block_id = 0;
496         struct storage_ext_callback *callback;
497         dd_list *elem;
498
499         if (!func)
500                 return -EINVAL;
501
502         DD_LIST_FOREACH(changed_list, elem, callback) {
503                 if (callback->func != func)
504                         continue;
505                 if (callback->block_id == 0)
506                         continue;
507
508                 return -EEXIST;
509         }
510
511         callback = (struct storage_ext_callback *)malloc(sizeof(struct storage_ext_callback));
512         if (!callback) {
513                 //LCOV_EXCL_START System Error
514                 _E("malloc() failed");
515                 return -ENOMEM;
516                 //LCOV_EXCL_STOP
517         }
518
519         conn = get_dbus_connection();
520         if (!conn) {
521                 //LCOV_EXCL_START System Error
522                 free(callback);
523                 _E("Failed to get dbus connection");
524                 return -EPERM;
525                 //LCOV_EXCL_STOP
526         }
527
528         block_id = g_dbus_connection_signal_subscribe(conn,
529                         NULL,
530                         STORAGE_EXT_IFACE_MANAGER,
531                         NULL,
532                         NULL,
533                         NULL,
534                         G_DBUS_SIGNAL_FLAGS_NONE,
535                         storage_ext_changed,
536                         NULL,
537                         NULL);
538         if (block_id == 0) {
539                 //LCOV_EXCL_START System Error
540                 free(callback);
541                 _E("Failed to subscrive bus signal");
542                 return -EPERM;
543                 //LCOV_EXCL_STOP
544         }
545
546         callback->func = func;
547         callback->data = data;
548         callback->block_id = block_id;
549
550         DD_LIST_APPEND(changed_list, callback);
551
552         return 0;
553 }
554
555 void storage_ext_unregister_device_change(storage_ext_changed_cb func)
556 {
557         GDBusConnection *conn;
558         struct storage_ext_callback *callback;
559         dd_list *elem;
560         dd_list *elem_n;
561
562         if (!func)
563                 return;
564
565         conn = get_dbus_connection();
566         if (!conn) {
567 //LCOV_EXCL_START System Error
568                 _E("fail to get dbus connection");
569                 return;
570 //LCOV_EXCL_STOP
571         }
572
573         DD_LIST_FOREACH_SAFE(changed_list, elem, elem_n, callback) {
574                 if (callback->func != func)
575                         continue;
576                 if (callback->block_id > 0)
577                         g_dbus_connection_signal_unsubscribe(conn, callback->block_id);
578
579                 DD_LIST_REMOVE(changed_list, callback);
580                 free(callback);
581         }
582 }
583
584 int storage_ext_get_device_info(int storage_id, storage_ext_device *info)
585 {
586         GVariant *result;
587
588         result = dbus_method_call_sync(STORAGE_EXT_BUS_NAME,
589                         STORAGE_EXT_PATH_MANAGER,
590                         STORAGE_EXT_IFACE_MANAGER,
591                         "GetDeviceInfo",
592                         g_variant_new("(i)", storage_id));
593         if (!result) {
594                 _E("There is no storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
595                 return -ENODEV;
596         }
597
598         if (g_variant_check_format_string(result, "(issssssisibii)", true)) {
599                 g_variant_get(result, "(issssssisibii)",
600                                 &info->type, &info->devnode, &info->syspath,
601                                 &info->fs_usage, &info->fs_type,
602                                 &info->fs_version, &info->fs_uuid,
603                                 &info->readonly, &info->mount_point,
604                                 &info->state, &info->primary,
605                                 &info->flags, &info->storage_id);
606         } else {
607                 _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
608                 return -ENODEV;
609         }
610
611         if (info->storage_id < 0) {
612                 _E("No storage with the storage id (%d)", storage_id); //LCOV_EXCL_LINE
613                 return -ENODEV;
614         }
615
616         g_variant_unref(result);
617
618         return 0;
619 }