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