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