From: Taeyoung Kim Date: Thu, 28 Apr 2016 06:40:48 +0000 (+0900) Subject: external: add storage change handler X-Git-Tag: submit/tizen/20160510.075001~5 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ebc865a7f34020f47d65aac30aa97782b88d05f4;p=platform%2Fcore%2Fsystem%2Flibstorage.git external: add storage change handler - 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 --- diff --git a/src/storage-external-dbus.c b/src/storage-external-dbus.c index 1f39362..2c63d76 100755 --- a/src/storage-external-dbus.c +++ b/src/storage-external-dbus.c @@ -33,8 +33,23 @@ #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); + } +} diff --git a/src/storage-external-dbus.h b/src/storage-external-dbus.h index 7a0867a..98522d5 100644 --- a/src/storage-external-dbus.h +++ b/src/storage-external-dbus.h @@ -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__ */ diff --git a/src/storage-external.c b/src/storage-external.c index c671645..6308c3b 100755 --- a/src/storage-external.c +++ b/src/storage-external.c @@ -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 diff --git a/src/storage-external.h b/src/storage-external.h index 45c4167..cf5217b 100755 --- a/src/storage-external.h +++ b/src/storage-external.h @@ -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__ */ diff --git a/src/storage.c b/src/storage.c index cc04ab5..e6a54f7 100644 --- a/src/storage.c +++ b/src/storage.c @@ -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; }