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