block: allow remove hotplug event to arrive at blockd
[project/fstools.git] / blockd.c
index 5c7fc897a783aacac9d9a1c31c387f5ac735bf92..edca896b14886ee5fceb38ea21edbac35e8454a0 100644 (file)
--- a/blockd.c
+++ b/blockd.c
@@ -1,4 +1,4 @@
-#include <sys/types.h>
+#define _GNU_SOURCE
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <sys/wait.h>
@@ -69,14 +69,27 @@ static const struct blobmsg_policy mount_policy[__MOUNT_MAX] = {
        [MOUNT_REMOVE] = { .name = "remove", .type = BLOBMSG_TYPE_INT32 },
 };
 
+enum {
+       INFO_DEVICE,
+       __INFO_MAX
+};
+
+static const struct blobmsg_policy info_policy[__INFO_MAX] = {
+       [INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
+};
+
 static char*
 _find_mount_point(char *device)
 {
-       char dev[32] = { 0 };
+       char *dev, *mp;
+
+       if (asprintf(&dev, "/dev/%s", device) == -1)
+               exit(ENOMEM);
 
-       snprintf(dev, sizeof(dev), "/dev/%s", device);
+       mp = find_mount_point(dev, 0);
+       free(dev);
 
-       return find_mount_point(dev, 0);
+       return mp;
 }
 
 static int
@@ -113,8 +126,10 @@ block(char *cmd, char *action, char *device)
        return ret;
 }
 
-static int hotplug_call_mount(const char *action, const char *devname,
-                             uloop_process_handler cb, void *priv)
+static int send_block_notification(struct ubus_context *ctx, const char *action,
+                           const char *devname);
+static int hotplug_call_mount(struct ubus_context *ctx, const char *action,
+                             const char *devname, uloop_process_handler cb, void *priv)
 {
        char * const argv[] = { "hotplug-call", "mount", NULL };
        struct hotplug_context *c = NULL;
@@ -152,6 +167,8 @@ static int hotplug_call_mount(const char *action, const char *devname,
                break;
        }
 
+       send_block_notification(ctx, action, devname);
+
        return 0;
 }
 
@@ -174,34 +191,46 @@ static void device_mount_remove_hotplug_cb(struct uloop_process *p, int stat)
        free(hctx);
 }
 
-static void device_mount_remove(struct device *device)
+static void device_mount_remove(struct ubus_context *ctx, struct device *device)
 {
-       hotplug_call_mount("remove", device->name,
+       hotplug_call_mount(ctx, "remove", device->name,
                           device_mount_remove_hotplug_cb, device);
 }
 
-static void device_mount_add(struct device *device)
+static void device_mount_add(struct ubus_context *ctx, struct device *device)
 {
        struct stat st;
-       char path[64];
+       char *path, *tmp;
+
+       if (asprintf(&path, "/tmp/run/blockd/%s", device->name) == -1)
+               exit(ENOMEM);
 
-       snprintf(path, sizeof(path), "/tmp/run/blockd/%s", device->name);
        if (!lstat(device->target, &st)) {
                if (S_ISLNK(st.st_mode))
                        unlink(device->target);
                else if (S_ISDIR(st.st_mode))
                        rmdir(device->target);
        }
+
+       tmp = strrchr(device->target, '/');
+       if (tmp && tmp != device->target && tmp != &device->target[strlen(path)-1]) {
+               *tmp = '\0';
+               mkdir_p(device->target, 0755);
+               *tmp = '/';
+       }
+
        if (symlink(path, device->target))
                ULOG_ERR("failed to symlink %s->%s (%d) - %m\n", device->target, path, errno);
        else
-               hotplug_call_mount("add", device->name, NULL, NULL);
+               hotplug_call_mount(ctx, "add", device->name, NULL, NULL);
+
+       free(path);
 }
 
 static int
 device_move(struct device *device_o, struct device *device_n)
 {
-       char path[64];
+       char *path;
 
        if (device_o->autofs != device_n->autofs)
                return -1;
@@ -211,9 +240,13 @@ device_move(struct device *device_o, struct device *device_n)
 
        if (device_o->autofs) {
                unlink(device_o->target);
-               snprintf(path, sizeof(path), "/tmp/run/blockd/%s", device_n->name);
+               if (asprintf(&path, "/tmp/run/blockd/%s", device_n->name) == -1)
+                       exit(ENOMEM);
+
                if (symlink(path, device_n->target))
                        ULOG_ERR("failed to symlink %s->%s (%d) - %m\n", device_n->target, path, errno);
+
+               free(path);
        } else {
                mkdir(device_n->target, 0755);
                if (mount(device_o->target, device_n->target, NULL, MS_MOVE, NULL))
@@ -243,7 +276,7 @@ block_hotplug(struct ubus_context *ctx, struct ubus_object *obj,
        struct blob_attr *_msg;
        char *devname, *_name;
        char *target = NULL, *__target;
-       char _target[32];
+       char *_target = NULL;
 
        blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
 
@@ -255,8 +288,10 @@ block_hotplug(struct ubus_context *ctx, struct ubus_object *obj,
        if (data[MOUNT_TARGET]) {
                target = blobmsg_get_string(data[MOUNT_TARGET]);
        } else {
-               snprintf(_target, sizeof(_target), "/mnt/%s",
-                        blobmsg_get_string(data[MOUNT_DEVICE]));
+               if (asprintf(&_target, "/mnt/%s",
+                            blobmsg_get_string(data[MOUNT_DEVICE])) == -1)
+                       exit(ENOMEM);
+
                target = _target;
        }
 
@@ -266,16 +301,23 @@ block_hotplug(struct ubus_context *ctx, struct ubus_object *obj,
                device = calloc_a(sizeof(*device), &_msg, blob_raw_len(msg),
                                  &_name, strlen(devname) + 1, &__target, strlen(target) + 1);
 
-       if (!device)
+       if (!device) {
+               if (_target)
+                       free(_target);
+
                return UBUS_STATUS_UNKNOWN_ERROR;
+       }
 
        if (data[MOUNT_REMOVE]) {
                vlist_delete(&devices, &device->node);
 
                if (device->autofs)
-                       device_mount_remove(device);
+                       device_mount_remove(ctx, device);
                else
                        free(device);
+
+               if (_target)
+                       free(_target);
        } else {
                struct device *old = vlist_find(&devices, devname, device, node);
 
@@ -287,18 +329,20 @@ block_hotplug(struct ubus_context *ctx, struct ubus_object *obj,
                strcpy(_name, devname);
                device->target = __target;
                strcpy(__target, target);
+               if (_target)
+                       free(_target);
 
                vlist_add(&devices, &device->node, device->name);
 
                if (old && !device_move(old, device)) {
                        if (device->autofs) {
-                               device_mount_remove(old);
-                               device_mount_add(device);
+                               device_mount_remove(ctx, old);
+                               device_mount_add(ctx, device);
                        } else {
                                block("mount", NULL, NULL);
                        }
                } else if (device->autofs) {
-                       device_mount_add(device);
+                       device_mount_add(ctx, device);
                }
        }
 
@@ -324,7 +368,7 @@ static int blockd_mount(struct ubus_context *ctx, struct ubus_object *obj,
        if (!device)
                return UBUS_STATUS_UNKNOWN_ERROR;
 
-       hotplug_call_mount("add", device->name, NULL, NULL);
+       hotplug_call_mount(ctx, "add", device->name, NULL, NULL);
 
        return 0;
 }
@@ -368,7 +412,7 @@ static int blockd_umount(struct ubus_context *ctx, struct ubus_object *obj,
        c->ctx = ctx;
        ubus_defer_request(ctx, req, &c->req);
 
-       err = hotplug_call_mount("remove", devname, blockd_umount_hotplug_cb, c);
+       err = hotplug_call_mount(ctx, "remove", devname, blockd_umount_hotplug_cb, c);
        if (err) {
                free(c);
                return UBUS_STATUS_UNKNOWN_ERROR;
@@ -377,35 +421,56 @@ static int blockd_umount(struct ubus_context *ctx, struct ubus_object *obj,
        return 0;
 }
 
+static void block_info_dump(struct blob_buf *b, struct device *device)
+{
+       struct blob_attr *v;
+       char *mp;
+       int rem;
+
+       blob_for_each_attr(v, device->msg, rem)
+               blobmsg_add_blob(b, v);
+
+       mp = _find_mount_point(device->name);
+       if (mp) {
+               blobmsg_add_string(b, "mount", mp);
+               free(mp);
+       } else if (device->autofs && device->target) {
+               blobmsg_add_string(b, "mount", device->target);
+       }
+}
+
 static int
 block_info(struct ubus_context *ctx, struct ubus_object *obj,
           struct ubus_request_data *req, const char *method,
           struct blob_attr *msg)
 {
-       struct device *device;
-       void *a;
+       struct blob_attr *data[__INFO_MAX];
+       struct device *device = NULL;
+
+       blobmsg_parse(info_policy, __INFO_MAX, data, blob_data(msg), blob_len(msg));
+
+       if (data[INFO_DEVICE]) {
+               device = vlist_find(&devices, blobmsg_get_string(data[INFO_DEVICE]), device, node);
+               if (!device)
+                       return UBUS_STATUS_INVALID_ARGUMENT;
+       }
 
        blob_buf_init(&bb, 0);
-       a = blobmsg_open_array(&bb, "devices");
-       vlist_for_each_element(&devices, device, node) {
-               void *t = blobmsg_open_table(&bb, "");
-               struct blob_attr *v;
-               char *mp;
-               int rem;
-
-               blob_for_each_attr(v, device->msg, rem)
-                       blobmsg_add_blob(&bb, v);
-
-               mp = _find_mount_point(device->name);
-               if (mp) {
-                       blobmsg_add_string(&bb, "mount", mp);
-                       free(mp);
-               } else if (device->autofs && device->target) {
-                       blobmsg_add_string(&bb, "mount", device->target);
+       if (device) {
+               block_info_dump(&bb, device);
+       } else {
+               void *a;
+
+               a = blobmsg_open_array(&bb, "devices");
+               vlist_for_each_element(&devices, device, node) {
+                       void *t;
+
+                       t = blobmsg_open_table(&bb, "");
+                       block_info_dump(&bb, device);
+                       blobmsg_close_table(&bb, t);
                }
-               blobmsg_close_table(&bb, t);
+               blobmsg_close_array(&bb, a);
        }
-       blobmsg_close_array(&bb, a);
        ubus_send_reply(ctx, req, bb.head);
 
        return 0;
@@ -415,7 +480,7 @@ static const struct ubus_method block_methods[] = {
        UBUS_METHOD("hotplug", block_hotplug, mount_policy),
        UBUS_METHOD("mount", blockd_mount, mount_policy),
        UBUS_METHOD("umount", blockd_umount, mount_policy),
-       UBUS_METHOD_NOARG("info", block_info),
+       UBUS_METHOD("info", block_info, info_policy),
 };
 
 static struct ubus_object_type block_object_type =
@@ -428,6 +493,27 @@ static struct ubus_object block_object = {
        .n_methods = ARRAY_SIZE(block_methods),
 };
 
+/* send ubus event for successful mounts, useful for procd triggers */
+static int send_block_notification(struct ubus_context *ctx, const char *action,
+                           const char *devname)
+{
+       struct blob_buf buf = { 0 };
+       char evname[16] = "mount.";
+       int err;
+
+       if (!ctx)
+               return -ENXIO;
+
+       strncat(evname, action, sizeof(evname) - 1);
+
+       blob_buf_init(&buf, 0);
+       blobmsg_add_string(&buf, "devname", devname);
+
+       err = ubus_notify(ctx, &block_object, evname, buf.head, -1);
+
+       return err;
+}
+
 static void
 ubus_connect_handler(struct ubus_context *ctx)
 {
@@ -531,8 +617,8 @@ static int autofs_mount(void)
 
        uloop_timeout_set(&autofs_expire_timer, AUTOFS_EXPIRE_TIMER);
 
-        fcntl(fd_autofs_write, F_SETFD, fcntl(fd_autofs_write, F_GETFD) | FD_CLOEXEC);
-        fcntl(fd_autofs_read.fd, F_SETFD, fcntl(fd_autofs_read.fd, F_GETFD) | FD_CLOEXEC);
+       fcntl(fd_autofs_write, F_SETFD, fcntl(fd_autofs_write, F_GETFD) | FD_CLOEXEC);
+       fcntl(fd_autofs_read.fd, F_SETFD, fcntl(fd_autofs_read.fd, F_GETFD) | FD_CLOEXEC);
 
        return 0;
 }
@@ -548,6 +634,9 @@ struct uloop_timeout startup = {
 
 int main(int argc, char **argv)
 {
+       /* make sure blockd is in it's own POSIX process group */
+       setpgrp();
+
        ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "blockd");
        uloop_init();