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