]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/start.c
commands: abstract cmd socket handling + logging
[mirror_lxc.git] / src / lxc / start.c
index 2929514e672dee35e154317ace4b0e8d0307c2ee..cd9dc18beed44a49b1aeab63ce3dc918011a368c 100644 (file)
@@ -46,7 +46,7 @@
 #include <sys/un.h>
 #include <sys/wait.h>
 
-#if HAVE_SYS_CAPABILITY_H
+#if HAVE_LIBCAP
 #include <sys/capability.h>
 #endif
 
 #include "caps.h"
 #include "cgroup.h"
 #include "commands.h"
+#include "commands_utils.h"
 #include "conf.h"
 #include "console.h"
 #include "error.h"
 #include "log.h"
+#include "lxccontainer.h"
 #include "lxclock.h"
 #include "lxcseccomp.h"
 #include "lxcutmp.h"
@@ -78,6 +80,7 @@
 #include "monitor.h"
 #include "namespace.h"
 #include "start.h"
+#include "storage_utils.h"
 #include "sync.h"
 #include "utils.h"
 #include "lsm/lsm.h"
@@ -187,18 +190,12 @@ static int match_fd(int fd)
        return (fd == 0 || fd == 1 || fd == 2);
 }
 
-/* Check for any fds we need to close.
- * - If fd_to_ignore != -1, then if we find that fd open we will ignore it.
- * - By default we warn about open fds we find.
- * - If closeall is true, we will close open fds.
- * - If lxc-start was passed "-C", then conf->close_all_fds will be true, in
- *   which case we also close all open fds.
- * - A daemonized container will always pass closeall=true.
- */
-int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore)
+int lxc_check_inherited(struct lxc_conf *conf, bool closeall,
+                       int *fds_to_ignore, size_t len_fds)
 {
        struct dirent *direntp;
        int fd, fddir;
+       size_t i;
        DIR *dir;
 
        if (conf && conf->close_all_fds)
@@ -207,7 +204,7 @@ int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore)
 restart:
        dir = opendir("/proc/self/fd");
        if (!dir) {
-               WARN("Failed to open directory: %m.");
+               WARN("Failed to open directory: %s.", strerror(errno));
                return -1;
        }
 
@@ -228,7 +225,12 @@ restart:
                        continue;
                }
 
-               if (fd == fddir || fd == lxc_log_fd || fd == fd_to_ignore)
+               for (i = 0; i < len_fds; i++)
+                       if (fds_to_ignore[i] == fd)
+                               break;
+
+               if (fd == fddir || fd == lxc_log_fd ||
+                   (i < len_fds && fd == fds_to_ignore[i]))
                        continue;
 
                if (current_config && fd == current_config->logfd)
@@ -333,7 +335,7 @@ static int signal_handler(int fd, uint32_t events, void *data,
         * by a process different from the container init.
         */
        if (siginfo.ssi_pid != *pid) {
-               WARN("Invalid pid for SIGCHLD. Received pid %d, expected pid %d.", siginfo.ssi_pid, *pid);
+               NOTICE("Received SIGCHLD from pid %d instead of container init %d.", siginfo.ssi_pid, *pid);
                return init_died ? 1 : 0;
        }
 
@@ -341,10 +343,116 @@ static int signal_handler(int fd, uint32_t events, void *data,
        return 1;
 }
 
-int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state)
+static int lxc_serve_state_clients(const char *name,
+                                  struct lxc_handler *handler,
+                                  lxc_state_t state)
 {
+       ssize_t ret;
+       struct lxc_list *cur, *next;
+       struct state_client *client;
+       struct lxc_msg msg = {.type = lxc_msg_state, .value = state};
+
+       process_lock();
+
+       /* Only set state under process lock held so that we don't cause
+       *  lxc_cmd_add_state_client() to miss a state.
+        */
        handler->state = state;
+       TRACE("set container state to %s", lxc_state2str(state));
+
+       if (lxc_list_empty(&handler->state_clients)) {
+               TRACE("no state clients registered");
+               process_unlock();
+               lxc_monitor_send_state(name, state, handler->lxcpath);
+               return 0;
+       }
+
+       strncpy(msg.name, name, sizeof(msg.name));
+       msg.name[sizeof(msg.name) - 1] = 0;
+
+       lxc_list_for_each_safe(cur, &handler->state_clients, next) {
+               client = cur->elem;
+
+               if (!client->states[state]) {
+                       TRACE("state %s not registered for state client %d",
+                             lxc_state2str(state), client->clientfd);
+                       continue;
+               }
+
+               TRACE("sending state %s to state client %d",
+                     lxc_state2str(state), client->clientfd);
+
+       again:
+               ret = send(client->clientfd, &msg, sizeof(msg), 0);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               goto again;
+
+                       ERROR("failed to send message to client");
+               }
+
+               /* kick client from list */
+               close(client->clientfd);
+               lxc_list_del(cur);
+               free(cur->elem);
+               free(cur);
+       }
+       process_unlock();
+
+       return 0;
+}
+
+static int lxc_serve_state_socket_pair(const char *name,
+                                      struct lxc_handler *handler,
+                                      lxc_state_t state)
+{
+       ssize_t ret;
+
+       if (!handler->backgrounded ||
+            handler->state_socket_pair[1] < 0 ||
+           state == STARTING)
+               return 0;
+
+       /* Close read end of the socket pair. */
+       close(handler->state_socket_pair[0]);
+       handler->state_socket_pair[0] = -1;
+
+       ret = lxc_abstract_unix_send_credential(handler->state_socket_pair[1],
+                                               &(int){state}, sizeof(int));
+       if (ret != sizeof(int))
+               SYSERROR("Failed to send state to %d",
+                        handler->state_socket_pair[1]);
+
+       TRACE("Sent container state \"%s\" to %d", lxc_state2str(state),
+             handler->state_socket_pair[1]);
+
+       /* Close write end of the socket pair. */
+       close(handler->state_socket_pair[1]);
+       handler->state_socket_pair[1] = -1;
+
+       return 0;
+}
+
+int lxc_set_state(const char *name, struct lxc_handler *handler,
+                 lxc_state_t state)
+{
+       int ret;
+
+       ret = lxc_serve_state_socket_pair(name, handler, state);
+       if (ret < 0) {
+               ERROR("Failed to synchronize via anonymous pair of unix sockets");
+               return -1;
+       }
+
+       ret = lxc_serve_state_clients(name, handler, state);
+       if (ret < 0)
+               return -1;
+
+       /* This function will try to connect to the legacy lxc-monitord state
+        * server and only exists for backwards compatibility.
+        */
        lxc_monitor_send_state(name, state, handler->lxcpath);
+
        return 0;
 }
 
@@ -375,7 +483,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
        }
 
        if (handler->conf->need_utmp_watch) {
-               #if HAVE_SYS_CAPABILITY_H
+               #if HAVE_LIBCAP
                if (lxc_utmp_mainloop_add(&descr, handler)) {
                        ERROR("Failed to add utmp handler to LXC mainloop.");
                        goto out_mainloop_open;
@@ -384,6 +492,7 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
                        DEBUG("Not starting utmp handler as CAP_SYS_BOOT cannot be dropped without capabilities support.");
                #endif
        }
+       TRACE("lxc mainloop is ready");
 
        return lxc_mainloop(&descr, -1);
 
@@ -396,14 +505,35 @@ out_sigfd:
        return -1;
 }
 
-struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char *lxcpath)
+void lxc_free_handler(struct lxc_handler *handler)
 {
-       int i;
+       if (handler->conf && handler->conf->maincmd_fd)
+               close(handler->conf->maincmd_fd);
+
+       if (handler->state_socket_pair[0] >= 0)
+               close(handler->state_socket_pair[0]);
+
+       if (handler->state_socket_pair[1] >= 0)
+               close(handler->state_socket_pair[1]);
+
+       if (handler->name)
+               free(handler->name);
+
+       handler->conf = NULL;
+       free(handler);
+}
+
+struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
+                                    const char *lxcpath, bool daemonize)
+{
+       int i, ret;
        struct lxc_handler *handler;
 
        handler = malloc(sizeof(*handler));
-       if (!handler)
+       if (!handler) {
+               ERROR("failed to allocate memory");
                return NULL;
+       }
 
        memset(handler, 0, sizeof(*handler));
 
@@ -411,31 +541,69 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
        handler->conf = conf;
        handler->lxcpath = lxcpath;
        handler->pinfd = -1;
+       handler->state_socket_pair[0] = handler->state_socket_pair[1] = -1;
+       lxc_list_init(&handler->state_clients);
 
        for (i = 0; i < LXC_NS_MAX; i++)
                handler->nsfd[i] = -1;
 
-       lsm_init();
-
        handler->name = strdup(name);
        if (!handler->name) {
-               ERROR("Failed to allocate memory.");
-               goto out_free;
+               ERROR("failed to allocate memory");
+               goto on_error;
+       }
+
+       if (daemonize && !handler->conf->reboot) {
+               /* Create socketpair() to synchronize on daemonized startup.
+                * When the container reboots we don't need to synchronize again
+                * currently so don't open another socketpair().
+                */
+               ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
+                                handler->state_socket_pair);
+               if (ret < 0) {
+                       ERROR("Failed to create anonymous pair of unix sockets");
+                       goto on_error;
+               }
+               TRACE("Created anonymous pair {%d,%d} of unix sockets",
+                     handler->state_socket_pair[0],
+                     handler->state_socket_pair[1]);
        }
 
-       if (lxc_cmd_init(name, handler, lxcpath))
-               goto out_free_name;
+       if (lxc_cmd_init(name, handler, lxcpath)) {
+               ERROR("failed to set up command socket");
+               goto on_error;
+       }
+
+       TRACE("unix domain socket %d for command server is ready",
+             handler->conf->maincmd_fd);
+
+       return handler;
+
+on_error:
+       lxc_free_handler(handler);
+
+       return NULL;
+}
+
+int lxc_init(const char *name, struct lxc_handler *handler)
+{
+       struct lxc_conf *conf = handler->conf;
+
+       lsm_init();
+       TRACE("initialized LSM");
 
        if (lxc_read_seccomp_config(conf) != 0) {
                ERROR("Failed loading seccomp policy.");
                goto out_close_maincmd_fd;
        }
+       TRACE("read seccomp policy");
 
        /* Begin by setting the state to STARTING. */
        if (lxc_set_state(name, handler, STARTING)) {
                ERROR("Failed to set state for container \"%s\" to \"%s\".", name, lxc_state2str(STARTING));
                goto out_close_maincmd_fd;
        }
+       TRACE("set container state to \"STARTING\"");
 
        /* Start of environment variable setup for hooks. */
        if (name && setenv("LXC_NAME", name, 1))
@@ -460,10 +628,13 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
                SYSERROR("Failed to set environment variable LXC_CGNS_AWARE=1.");
        /* End of environment variable setup for hooks. */
 
+       TRACE("set environment variables");
+
        if (run_lxc_hooks(name, "pre-start", conf, handler->lxcpath, NULL)) {
                ERROR("Failed to run lxc.hook.pre-start for container \"%s\".", name);
                goto out_aborting;
        }
+       TRACE("ran pre-start hooks");
 
        /* The signal fd has to be created before forking otherwise if the child
         * process exits before we setup the signal fd, the event will be lost
@@ -474,20 +645,23 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
                ERROR("Failed to setup SIGCHLD fd handler.");
                goto out_delete_tty;
        }
+       TRACE("set up signal fd");
 
        /* Do this after setting up signals since it might unblock SIGWINCH. */
        if (lxc_console_create(conf)) {
                ERROR("Failed to create console for container \"%s\".", name);
                goto out_restore_sigmask;
        }
+       TRACE("created console");
 
-       if (ttys_shift_ids(conf) < 0) {
+       if (lxc_ttys_shift_ids(conf) < 0) {
                ERROR("Failed to shift tty into container.");
                goto out_restore_sigmask;
        }
+       TRACE("shifted tty ids");
 
-       INFO("Container \"%s\" is initialized.", name);
-       return handler;
+       INFO("container \"%s\" is initialized", name);
+       return 0;
 
 out_restore_sigmask:
        sigprocmask(SIG_SETMASK, &handler->oldmask, NULL);
@@ -498,19 +672,15 @@ out_aborting:
 out_close_maincmd_fd:
        close(conf->maincmd_fd);
        conf->maincmd_fd = -1;
-out_free_name:
-       free(handler->name);
-       handler->name = NULL;
-out_free:
-       free(handler);
-       return NULL;
+       return -1;
 }
 
 void lxc_fini(const char *name, struct lxc_handler *handler)
 {
        int i, rc;
+       struct lxc_list *cur, *next;
        pid_t self = getpid();
-       char *namespaces[LXC_NS_MAX+1];
+       char *namespaces[LXC_NS_MAX + 1];
        size_t namespace_count = 0;
 
        /* The STOPPING state is there for future cleanup code which can take
@@ -572,8 +742,23 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
 
        lxc_console_delete(&handler->conf->console);
        lxc_delete_tty(&handler->conf->tty_info);
+
+       /* close the command socket */
        close(handler->conf->maincmd_fd);
        handler->conf->maincmd_fd = -1;
+
+       /* The command socket is now closed, no more state clients can register
+        * themselves from now on. So free the list of state clients.
+        */
+       lxc_list_for_each_safe(cur, &handler->state_clients, next) {
+               struct state_client *client = cur->elem;
+               /* close state client socket */
+               close(client->clientfd);
+               lxc_list_del(cur);
+               free(cur->elem);
+               free(cur);
+       }
+
        free(handler->name);
        if (handler->ttysock[0] != -1) {
                close(handler->ttysock[0]);
@@ -658,7 +843,7 @@ static int must_drop_cap_sys_boot(struct lxc_conf *conf)
                return -1;
        }
        if (wait(&status) < 0) {
-               SYSERROR("Unexpected wait error: %m.");
+               SYSERROR("Unexpected wait error: %s.", strerror(errno));
                return -1;
        }
 
@@ -787,7 +972,7 @@ static int do_start(void *data)
                goto out_warn_father;
        }
 
-       #if HAVE_SYS_CAPABILITY_H
+       #if HAVE_LIBCAP
        if (handler->conf->need_utmp_watch) {
                if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) {
                        SYSERROR("Failed to remove the CAP_SYS_BOOT capability.");
@@ -898,8 +1083,8 @@ static int do_start(void *data)
                 * further above. Only drop groups if we can, so ensure that we
                 * have necessary privilege.
                 */
-               #if HAVE_SYS_CAPABILITY_H
-               have_cap_setgid = lxc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
+               #if HAVE_LIBCAP
+               have_cap_setgid = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
                #else
                have_cap_setgid = false;
                #endif
@@ -1008,22 +1193,16 @@ static int save_phys_nics(struct lxc_conf *conf)
        return 0;
 }
 
-static int recv_fd(int sock, int *fd)
-{
-       if (lxc_abstract_unix_recv_fd(sock, fd, NULL, 0) < 0) {
-               SYSERROR("Error receiving tty file descriptor from child process.");
-               return -1;
-       }
-       if (*fd == -1)
-               return -1;
-       return 0;
-}
-
-static int recv_ttys_from_child(struct lxc_handler *handler)
+static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
 {
+       int i;
+       int *ttyfds;
+       struct lxc_pty_info *pty_info;
+       int ret = -1;
+       int sock = handler->ttysock[1];
        struct lxc_conf *conf = handler->conf;
-       int i, sock = handler->ttysock[1];
        struct lxc_tty_info *tty_info = &conf->tty_info;
+       size_t num_ttyfds = (2 * conf->tty);
 
        if (!conf->tty)
                return 0;
@@ -1032,18 +1211,31 @@ static int recv_ttys_from_child(struct lxc_handler *handler)
        if (!tty_info->pty_info)
                return -1;
 
-       for (i = 0; i < conf->tty; i++) {
-               struct lxc_pty_info *pty_info = &tty_info->pty_info[i];
+       ttyfds = malloc(num_ttyfds * sizeof(int));
+       if (!ttyfds)
+               return -1;
+
+       ret = lxc_abstract_unix_recv_fds(sock, ttyfds, num_ttyfds, NULL, 0);
+       for (i = 0; (ret >= 0 && *ttyfds != -1) && (i < num_ttyfds); i++) {
+               pty_info = &tty_info->pty_info[i / 2];
                pty_info->busy = 0;
-               if (recv_fd(sock, &pty_info->slave) < 0 ||
-                   recv_fd(sock, &pty_info->master) < 0) {
-                       ERROR("Error receiving tty info from child process.");
-                       return -1;
-               }
+               pty_info->slave = ttyfds[i++];
+               pty_info->master = ttyfds[i];
+               TRACE("received pty with master fd %d and slave fd %d from "
+                     "parent", pty_info->master, pty_info->slave);
        }
+
        tty_info->nbtty = conf->tty;
 
-       return 0;
+       free(ttyfds);
+
+       if (ret < 0)
+               ERROR("failed to receive %d ttys from child: %s", conf->tty,
+                     strerror(errno));
+       else
+               TRACE("received %d ttys from child", conf->tty);
+
+       return ret;
 }
 
 void resolve_clone_flags(struct lxc_handler *handler)
@@ -1071,6 +1263,13 @@ void resolve_clone_flags(struct lxc_handler *handler)
                INFO("Inheriting a UTS namespace.");
 }
 
+/* lxc_spawn() performs crucial setup tasks and clone()s the new process which
+ * exec()s the requested container binary.
+ * Note that lxc_spawn() runs in the parent namespaces. Any operations performed
+ * right here should be double checked if they'd pose a security risk. (For
+ * example, any {u}mount() operations performed here will be reflected on the
+ * host!)
+ */
 static int lxc_spawn(struct lxc_handler *handler)
 {
        int failed_before_rename = 0;
@@ -1079,8 +1278,12 @@ static int lxc_spawn(struct lxc_handler *handler)
        int saved_ns_fd[LXC_NS_MAX];
        int preserve_mask = 0, i, flags;
        int netpipepair[2], nveths;
+       bool wants_to_map_ids;
+       struct lxc_list *id_map;
 
        netpipe = -1;
+       id_map = &handler->conf->id_map;
+       wants_to_map_ids = !lxc_list_empty(id_map);
 
        for (i = 0; i < LXC_NS_MAX; i++)
                if (handler->conf->inherit_ns_fd[i] != -1)
@@ -1113,7 +1316,7 @@ static int lxc_spawn(struct lxc_handler *handler)
                        /* That should be done before the clone because we will
                         * fill the netdev index and use them in the child.
                         */
-                       if (lxc_create_network(handler)) {
+                       if (lxc_setup_networks_in_parent_namespaces(handler)) {
                                ERROR("Failed to create the network.");
                                lxc_sync_fini(handler);
                                return -1;
@@ -1142,7 +1345,7 @@ static int lxc_spawn(struct lxc_handler *handler)
         * it readonly.
         * If the container is unprivileged then skip rootfs pinning.
         */
-       if (lxc_list_empty(&handler->conf->id_map)) {
+       if (!wants_to_map_ids) {
                handler->pinfd = pin_rootfs(handler->conf->rootfs.path);
                if (handler->pinfd == -1)
                        INFO("Failed to pin the rootfs for container \"%s\".", handler->name);
@@ -1196,7 +1399,7 @@ static int lxc_spawn(struct lxc_handler *handler)
         * mapped to something else on the host.) later to become a valid uid
         * again.
         */
-       if (lxc_map_ids(&handler->conf->id_map, handler->pid)) {
+       if (wants_to_map_ids && lxc_map_ids(id_map, handler->pid)) {
                ERROR("Failed to set up id mapping.");
                goto out_delete_net;
        }
@@ -1261,6 +1464,11 @@ static int lxc_spawn(struct lxc_handler *handler)
        if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE))
                goto out_delete_net;
 
+       if (!lxc_list_empty(&handler->conf->limits) && setup_resource_limits(&handler->conf->limits, handler->pid)) {
+               ERROR("failed to setup resource limits for '%s'", name);
+               goto out_delete_net;
+       }
+
        if (!cgroup_setup_limits(handler, true)) {
                ERROR("Failed to setup the devices cgroup for container \"%s\".", name);
                goto out_delete_net;
@@ -1270,7 +1478,7 @@ static int lxc_spawn(struct lxc_handler *handler)
        cgroups_connected = false;
 
        /* Read tty fds allocated by child. */
-       if (recv_ttys_from_child(handler) < 0) {
+       if (lxc_recv_ttys_from_child(handler) < 0) {
                ERROR("Failed to receive tty info from child process.");
                goto out_delete_net;
        }
@@ -1284,9 +1492,6 @@ static int lxc_spawn(struct lxc_handler *handler)
        if (lxc_sync_barrier_child(handler, LXC_SYNC_POST_CGROUP))
                return -1;
 
-       if (detect_shared_rootfs())
-               umount2(handler->conf->rootfs.mount, MNT_DETACH);
-
        if (handler->ops->post_start(handler, handler->data))
                goto out_abort;
 
@@ -1317,17 +1522,16 @@ out_abort:
        return -1;
 }
 
-int __lxc_start(const char *name, struct lxc_conf *conf,
+int __lxc_start(const char *name, struct lxc_handler *handler,
                struct lxc_operations* ops, void *data, const char *lxcpath,
                bool backgrounded)
 {
-       struct lxc_handler *handler;
-       int err = -1;
        int status;
+       int err = -1;
        bool removed_all_netdevs = true;
+       struct lxc_conf *conf = handler->conf;
 
-       handler = lxc_init(name, conf, lxcpath);
-       if (!handler) {
+       if (lxc_init(name, handler) < 0) {
                ERROR("Failed to initialize container \"%s\".", name);
                return -1;
        }
@@ -1337,7 +1541,7 @@ int __lxc_start(const char *name, struct lxc_conf *conf,
        handler->netnsfd = -1;
 
        if (must_drop_cap_sys_boot(handler->conf)) {
-               #if HAVE_SYS_CAPABILITY_H
+               #if HAVE_LIBCAP
                DEBUG("Dropping CAP_SYS_BOOT capability.");
                #else
                DEBUG("Not dropping CAP_SYS_BOOT capability as capabilities aren't supported.");
@@ -1474,15 +1678,15 @@ static struct lxc_operations start_ops = {
        .post_start = post_start
 };
 
-int lxc_start(const char *name, char *const argv[], struct lxc_conf *conf,
+int lxc_start(const char *name, char *const argv[], struct lxc_handler *handler,
              const char *lxcpath, bool backgrounded)
 {
        struct start_args start_arg = {
                .argv = argv,
        };
 
-       conf->need_utmp_watch = 1;
-       return __lxc_start(name, conf, &start_ops, &start_arg, lxcpath, backgrounded);
+       handler->conf->need_utmp_watch = 1;
+       return __lxc_start(name, handler, &start_ops, &start_arg, lxcpath, backgrounded);
 }
 
 static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
@@ -1520,7 +1724,8 @@ static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
        }
 
        if (am_unpriv())
-               ret = userns_exec_1(handler->conf, lxc_rmdir_onedev_wrapper, destroy);
+               ret = userns_exec_1(handler->conf, lxc_rmdir_onedev_wrapper,
+                                   destroy, "lxc_rmdir_onedev_wrapper");
        else
                ret = lxc_rmdir_onedev(destroy, NULL);
 
@@ -1539,7 +1744,8 @@ static int lxc_rmdir_onedev_wrapper(void *data)
 
 static bool do_destroy_container(struct lxc_conf *conf) {
        if (am_unpriv()) {
-               if (userns_exec_1(conf, bdev_destroy_wrapper, conf) < 0)
+               if (userns_exec_1(conf, bdev_destroy_wrapper, conf,
+                                 "bdev_destroy_wrapper") < 0)
                        return false;
                return true;
        }