external: add storage change handler 23/67723/3
authorTaeyoung Kim <ty317.kim@samsung.com>
Thu, 28 Apr 2016 06:40:48 +0000 (15:40 +0900)
committerTaeyoung Kim <ty317.kim@samsung.com>
Fri, 29 Apr 2016 01:24:12 +0000 (10:24 +0900)
- Storage change handler functions are added.
  They trace dbus signal from deviced. If storages are
  added/removed/changed, dbus signal is broadcasted.
  Thus the callbacks can be called according to the
  storage state change.

Change-Id: Ib60f9f5b3bc316bb1c276dc0a7d30b881ca9da3a
Signed-off-by: Taeyoung Kim <ty317.kim@samsung.com>
src/storage-external-dbus.c
src/storage-external-dbus.h
src/storage-external.c
src/storage-external.h
src/storage.c

index 1f39362..2c63d76 100755 (executable)
 
 #define STORAGE_EXT_GET_LIST       "GetDeviceList"
 
+#define STORAGE_EXT_OBJECT_ADDED   "ObjectAdded"
+#define STORAGE_EXT_OBJECT_REMOVED "ObjectRemoved"
+#define STORAGE_EXT_DEVICE_CHANGED "DeviceChanged"
+
 #define DBUS_REPLY_TIMEOUT (-1)
 
+#define DEV_PREFIX           "/dev/"
+
+struct storage_ext_callback {
+       storage_ext_changed_cb func;
+       void *data;
+       guint block_id;
+       guint blockmanager_id;
+};
+
+static dd_list *changed_list;
+
 static void storage_ext_release_internal(storage_ext_device *dev)
 {
        if (!dev)
@@ -193,3 +208,239 @@ out:
        g_variant_unref(result);
        return ret;
 }
+
+static char *get_devnode_from_path(char *path)
+{
+       if (!path)
+               return NULL;
+       /* 1 means '/' */
+       return path + strlen(STORAGE_EXT_PATH_DEVICES) + 1;
+}
+
+static void storage_ext_object_path_changed(enum storage_ext_state state,
+               GVariant *params, gpointer user_data)
+{
+       storage_ext_device *dev = NULL;
+       dd_list *elem;
+       struct storage_ext_callback *callback;
+       char *path = NULL;
+       char *devnode;
+       int ret;
+
+       if (!params)
+               return;
+
+       g_variant_get(params, "(s)", &path);
+
+       devnode = get_devnode_from_path(path);
+       if (!devnode)
+               goto out;
+
+       dev = calloc(1, sizeof(storage_ext_device *));
+       if (!dev)
+               goto out;
+
+       dev->devnode = strdup(devnode);
+       if (dev->devnode == NULL) {
+               _E("strdup() failed");
+               goto out;
+       }
+
+       DD_LIST_FOREACH(changed_list, elem, callback) {
+               if (!callback->func)
+                       continue;
+               ret = callback->func(dev, state, callback->data);
+               if (ret < 0)
+                       _E("Failed to call callback for devnode(%s, %d)", devnode, ret);
+       }
+
+out:
+       if (dev) {
+               free(dev->devnode);
+               free(dev);
+       }
+       free(path);
+}
+
+static void storage_ext_device_added(GVariant *params, gpointer user_data)
+{
+       storage_ext_object_path_changed(STORAGE_EXT_ADDED, params, user_data);
+}
+
+static void storage_ext_device_removed(GVariant *params, gpointer user_data)
+{
+       storage_ext_object_path_changed(STORAGE_EXT_REMOVED, params, user_data);
+}
+
+static void storage_ext_device_changed(GVariant *params, gpointer user_data)
+{
+       storage_ext_device *dev;
+       dd_list *elem;
+       struct storage_ext_callback *callback;
+       int ret;
+
+       if (!params)
+               return;
+
+       dev = calloc(1, sizeof(storage_ext_device));
+       if (!dev)
+               return;
+
+       g_variant_get(params, "(issssssisibii)",
+                       &dev->type,
+                       &dev->devnode,
+                       &dev->syspath,
+                       &dev->fs_usage,
+                       &dev->fs_type,
+                       &dev->fs_version,
+                       &dev->fs_uuid,
+                       &dev->readonly,
+                       &dev->mount_point,
+                       &dev->state,
+                       &dev->primary,
+                       &dev->flags,
+                       &dev->storage_id);
+
+       DD_LIST_FOREACH(changed_list, elem, callback) {
+               if (!callback->func)
+                       continue;
+               ret = callback->func(dev, STORAGE_EXT_CHANGED, callback->data);
+               if (ret < 0)
+                       _E("Failed to call callback for devnode(%s, %d)", dev->devnode, ret);
+       }
+
+       storage_ext_release_device(&dev);
+}
+
+static void storage_ext_changed(GDBusConnection *conn,
+               const gchar *sender,
+               const gchar *path,
+               const gchar *iface,
+               const gchar *signal,
+               GVariant *params,
+               gpointer user_data)
+{
+       size_t iface_len, signal_len;
+
+       if (!params || !sender || !path || !iface || !signal)
+               return;
+
+       iface_len = strlen(iface) + 1;
+       signal_len = strlen(signal) + 1;
+
+       if (!strncmp(iface, STORAGE_EXT_IFACE_MANAGER, iface_len)) {
+               if (!strncmp(signal, STORAGE_EXT_OBJECT_ADDED, signal_len))
+                       storage_ext_device_added(params, user_data);
+               else if (!strncmp(signal, STORAGE_EXT_OBJECT_REMOVED, signal_len))
+                       storage_ext_device_removed(params, user_data);
+               return;
+       }
+
+       if (!strncmp(iface, STORAGE_EXT_IFACE, iface_len) &&
+               !strncmp(signal, STORAGE_EXT_DEVICE_CHANGED, signal_len)) {
+               storage_ext_device_changed(params, user_data);
+               return;
+       }
+}
+
+int storage_ext_register_device_change(storage_ext_changed_cb func, void *data)
+{
+       GDBusConnection *conn;
+       guint block_id = NULL, blockmanager_id = NULL;
+       struct storage_ext_callback *callback;
+       dd_list *elem;
+
+       if (!func)
+               return -EINVAL;
+
+       DD_LIST_FOREACH(changed_list, elem, callback) {
+               if (callback->func != func)
+                       continue;
+               if (callback->block_id == 0 || callback->blockmanager_id == 0)
+                       continue;
+
+               return -EEXIST;
+       }
+
+       callback = (struct storage_ext_callback *)malloc(sizeof(struct storage_ext_callback));
+       if (!callback) {
+               _E("malloc() failed");
+               return -ENOMEM;
+       }
+
+       conn = get_dbus_connection();
+       if (!conn) {
+               free(callback);
+               _E("Failed to get dbus connection");
+               return -EPERM;
+       }
+
+       block_id = g_dbus_connection_signal_subscribe(conn,
+                       STORAGE_EXT_BUS_NAME,
+                       STORAGE_EXT_IFACE,
+                       STORAGE_EXT_DEVICE_CHANGED,
+                       NULL,
+                       NULL,
+                       G_DBUS_SIGNAL_FLAGS_NONE,
+                       storage_ext_changed,
+                       NULL,
+                       NULL);
+       if (block_id == 0) {
+               free(callback);
+               _E("Failed to subscrive bus signal");
+               return -EPERM;
+       }
+
+       blockmanager_id = g_dbus_connection_signal_subscribe(conn,
+                       STORAGE_EXT_BUS_NAME,
+                       STORAGE_EXT_IFACE_MANAGER,
+                       NULL,
+                       STORAGE_EXT_PATH_MANAGER,
+                       NULL,
+                       G_DBUS_SIGNAL_FLAGS_NONE,
+                       storage_ext_changed,
+                       NULL,
+                       NULL);
+       if (blockmanager_id == 0) {
+               free(callback);
+               _E("Failed to subscrive bus signal");
+               return -EPERM;
+       }
+
+       callback->func = func;
+       callback->data = data;
+       callback->block_id = block_id;
+       callback->blockmanager_id = blockmanager_id;
+
+       DD_LIST_APPEND(changed_list, callback);
+
+       return 0;
+}
+
+void storage_ext_unregister_device_change(storage_ext_changed_cb func)
+{
+       GDBusConnection *conn;
+       struct storage_ext_callback *callback;
+       dd_list *elem;
+
+       if (!func)
+               return;
+
+       conn = get_dbus_connection();
+       if (!conn) {
+               _E("fail to get dbus connection");
+               return;
+       }
+
+       DD_LIST_FOREACH(changed_list, elem, callback) {
+               if (callback->func != func)
+                       continue;
+               if (callback->block_id > 0)
+                       g_dbus_connection_signal_unsubscribe(conn, callback->block_id);
+               if (callback->blockmanager_id > 0)
+                       g_dbus_connection_signal_unsubscribe(conn, callback->blockmanager_id);
+
+               DD_LIST_REMOVE(changed_list, callback);
+               free(callback);
+       }
+}
index 7a0867a..98522d5 100644 (file)
@@ -73,8 +73,13 @@ typedef struct _storage_ext_device {
        int storage_id;
 } storage_ext_device;
 
+typedef int (*storage_ext_changed_cb)(storage_ext_device *dev, enum storage_ext_state state, void *data);
+
 void storage_ext_release_device(storage_ext_device **dev);
 void storage_ext_release_list(dd_list **list);
 int storage_ext_get_list(dd_list **list);
 
+int storage_ext_register_device_change(storage_ext_changed_cb func, void *data);
+void storage_ext_unregister_device_change(storage_ext_changed_cb func);
+
 #endif /* __STORAGE_EXTERNAL_DBUS_H__ */
index c671645..6308c3b 100755 (executable)
@@ -28,6 +28,8 @@
 #include "log.h"
 #include "storage-external-dbus.h"
 
+static dd_list *cb_list[STORAGE_CALLBACK_MAX];
+
 static int storage_ext_get_dev_state(storage_ext_device *dev,
                enum storage_ext_state blk_state,
                storage_state_e *state)
@@ -91,4 +93,102 @@ int storage_ext_foreach_device_list(storage_device_supported_cb callback, void *
        if (list)
                storage_ext_release_list(&list);
        return 0;
+}
+
+static int storage_ext_state_changed(storage_ext_device *dev, enum storage_ext_state blk_state, void *data)
+{
+       enum storage_cb_type type = (enum storage_cb_type)data;
+       struct storage_cb_info *cb_info;
+       dd_list *elem;
+       storage_state_e state;
+       int ret;
+
+       if (!dev)
+               return -EINVAL;
+
+       if (type != STORAGE_CALLBACK_STATE)
+               return 0;
+
+       ret = storage_ext_get_dev_state(dev, blk_state, &state);
+       if (ret < 0) {
+               _E("Failed to get storage state (devnode:%s, ret:%d)", dev->devnode, ret);
+               return ret;
+       }
+
+       DD_LIST_FOREACH(cb_list[STORAGE_CALLBACK_STATE], elem, cb_info)
+               cb_info->state_cb(cb_info->id, state, cb_info->user_data);
+
+       return 0;
+}
+
+int storage_ext_register_cb(enum storage_cb_type type, struct storage_cb_info *info)
+{
+       struct storage_cb_info *cb_info;
+       dd_list *elem;
+       int ret, n;
+
+       if (type < 0 || type >= STORAGE_CALLBACK_MAX)
+               return -EINVAL;
+
+       if (!info)
+               return -EINVAL;
+
+       /* check if it is the first request */
+       n = DD_LIST_LENGTH(cb_list[type]);
+       if (n == 0) {
+               ret = storage_ext_register_device_change(storage_ext_state_changed, (void *)type);
+               if (ret < 0)
+                       return -EPERM;
+       }
+
+       /* check for the same request */
+       DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
+               if (cb_info->id == info->id &&
+                   cb_info->state_cb == info->state_cb)
+                       return -EEXIST;
+       }
+
+       /* add device changed callback to list (local) */
+       cb_info = malloc(sizeof(struct storage_cb_info));
+       if (!cb_info)
+               return -errno;
+
+       memcpy(cb_info, info, sizeof(struct storage_cb_info));
+       DD_LIST_APPEND(cb_list[type], cb_info);
+
+       return 0;
+}
+
+int storage_ext_unregister_cb(enum storage_cb_type type, struct storage_cb_info *info)
+{
+       struct storage_cb_info *cb_info;
+       dd_list *elem;
+       int n;
+
+       if (type < 0 || type >= STORAGE_CALLBACK_MAX)
+               return -EINVAL;
+
+       if (!info)
+               return -EINVAL;
+
+       /* search for the same element with callback */
+       DD_LIST_FOREACH(cb_list[type], elem, cb_info) {
+               if (cb_info->id == info->id &&
+                   cb_info->state_cb == info->state_cb)
+                       break;
+       }
+
+       if (!cb_info)
+               return -EINVAL;
+
+       /* remove device callback from list (local) */
+       DD_LIST_REMOVE(cb_list[type], cb_info);
+       free(cb_info);
+
+       /* check if this callback is last element */
+       n = DD_LIST_LENGTH(cb_list[type]);
+       if (n == 0)
+               storage_ext_unregister_device_change(storage_ext_state_changed);
+
+       return 0;
 }
\ No newline at end of file
index 45c4167..cf5217b 100755 (executable)
@@ -22,5 +22,7 @@
 #include "storage-external-dbus.h"
 
 int storage_ext_foreach_device_list(storage_device_supported_cb callback, void *user_data);
+int storage_ext_register_cb(enum storage_cb_type type, struct storage_cb_info *info);
+int storage_ext_unregister_cb(enum storage_cb_type type, struct storage_cb_info *info);
 
 #endif /* __STORAGE_EXTERNAL_H__ */
index cc04ab5..e6a54f7 100644 (file)
@@ -215,6 +215,8 @@ API int storage_get_state(int storage_id, storage_state_e *state)
 API int storage_set_state_changed_cb(int storage_id, storage_state_changed_cb callback, void *user_data)
 {
        const struct storage_ops *st;
+       struct storage_cb_info info;
+       int ret;
        dd_list *elem;
 
        if (!callback) {
@@ -228,6 +230,15 @@ API int storage_set_state_changed_cb(int storage_id, storage_state_changed_cb ca
                        return STORAGE_ERROR_NONE;
 
        /* external storage */
+       info.id = storage_id;
+       info.state_cb = callback;
+       info.user_data = user_data;
+
+       ret = storage_ext_register_cb(STORAGE_CALLBACK_STATE, &info);
+       if (ret < 0) {
+               _E("Failed to register callback : id(%d)", storage_id);
+               return STORAGE_ERROR_OPERATION_FAILED;
+       }
 
        return STORAGE_ERROR_NONE;
 }
@@ -235,6 +246,8 @@ API int storage_set_state_changed_cb(int storage_id, storage_state_changed_cb ca
 API int storage_unset_state_changed_cb(int storage_id, storage_state_changed_cb callback)
 {
        const struct storage_ops *st;
+       struct storage_cb_info info;
+       int ret;
        dd_list *elem;
 
        if (!callback) {
@@ -247,6 +260,16 @@ API int storage_unset_state_changed_cb(int storage_id, storage_state_changed_cb
                if (st->storage_id == storage_id)
                        return STORAGE_ERROR_NONE;
 
+       /* external storage */
+       info.id = storage_id;
+       info.state_cb = callback;
+
+       ret = storage_ext_unregister_cb(STORAGE_CALLBACK_STATE, &info);
+       if (ret < 0) {
+               _E("Failed to unregister callback : id(%d)", storage_id);
+               return STORAGE_ERROR_OPERATION_FAILED;
+       }
+
        return STORAGE_ERROR_NONE;
 }