Add mkdirat utils and runas wrappers
[lttng-tools.git] / src / common / runas.c
index 4bd356a0499f2119a227a69b324ab5248d7ba8a0..bba0aa49d47292d383dd7e09b968779168bc8d44 100644 (file)
@@ -49,7 +49,7 @@ struct run_as_data;
 struct run_as_ret;
 typedef int (*run_as_fct)(struct run_as_data *data, struct run_as_ret *ret_value);
 
-struct run_as_mkdir_data {
+struct run_as_mkdirat_data {
        char path[PATH_MAX];
        mode_t mode;
 };
@@ -77,7 +77,7 @@ struct run_as_extract_sdt_probe_offsets_data {
        char provider_name[LTTNG_SYMBOL_NAME_LEN];
 };
 
-struct run_as_mkdir_ret {
+struct run_as_mkdirat_ret {
        int ret;
 };
 
@@ -104,10 +104,12 @@ struct run_as_extract_sdt_probe_offsets_ret {
 
 enum run_as_cmd {
        RUN_AS_MKDIR,
+       RUN_AS_MKDIRAT,
+       RUN_AS_MKDIR_RECURSIVE,
+       RUN_AS_MKDIRAT_RECURSIVE,
        RUN_AS_OPEN,
        RUN_AS_UNLINK,
        RUN_AS_RMDIR_RECURSIVE,
-       RUN_AS_MKDIR_RECURSIVE,
        RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET,
        RUN_AS_EXTRACT_SDT_PROBE_OFFSETS,
 };
@@ -116,7 +118,7 @@ struct run_as_data {
        enum run_as_cmd cmd;
        int fd;
        union {
-               struct run_as_mkdir_data mkdir;
+               struct run_as_mkdirat_data mkdirat;
                struct run_as_open_data open;
                struct run_as_unlink_data unlink;
                struct run_as_rmdir_recursive_data rmdir_recursive;
@@ -145,7 +147,7 @@ struct run_as_data {
 struct run_as_ret {
        int fd;
        union {
-               struct run_as_mkdir_ret mkdir;
+               struct run_as_mkdirat_ret mkdirat;
                struct run_as_open_ret open;
                struct run_as_unlink_ret unlink;
                struct run_as_rmdir_recursive_ret rmdir_recursive;
@@ -181,35 +183,49 @@ int use_clone(void)
 }
 #endif
 
-LTTNG_HIDDEN
-int _utils_mkdir_recursive_unsafe(const char *path, mode_t mode);
-
 /*
  * Create recursively directory using the FULL path.
  */
 static
-int _mkdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
+int _mkdirat_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
 {
        const char *path;
        mode_t mode;
+       struct lttng_directory_handle handle;
 
-       path = data->u.mkdir.path;
-       mode = data->u.mkdir.mode;
+       path = data->u.mkdirat.path;
+       mode = data->u.mkdirat.mode;
 
+       (void) lttng_directory_handle_init_from_dirfd(&handle, data->fd);
        /* Safe to call as we have transitioned to the requested uid/gid. */
-       ret_value->u.mkdir.ret = _utils_mkdir_recursive_unsafe(path, mode);
+       ret_value->u.mkdirat.ret =
+                       lttng_directory_handle_create_subdirectory_recursive(
+                                       &handle, path, mode);
        ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.mkdir.ret) ? true : false;
-       return ret_value->u.mkdir.ret;
+       ret_value->_error = (ret_value->u.mkdirat.ret) ? true : false;
+       lttng_directory_handle_fini(&handle);
+       return ret_value->u.mkdirat.ret;
 }
 
 static
-int _mkdir(struct run_as_data *data, struct run_as_ret *ret_value)
+int _mkdirat(struct run_as_data *data, struct run_as_ret *ret_value)
 {
-       ret_value->u.mkdir.ret = mkdir(data->u.mkdir.path, data->u.mkdir.mode);
+       const char *path;
+       mode_t mode;
+       struct lttng_directory_handle handle;
+
+       path = data->u.mkdirat.path;
+       mode = data->u.mkdirat.mode;
+
+       (void) lttng_directory_handle_init_from_dirfd(&handle, data->fd);
+       /* Safe to call as we have transitioned to the requested uid/gid. */
+       ret_value->u.mkdirat.ret =
+                       lttng_directory_handle_create_subdirectory(
+                                       &handle, path, mode);
        ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.mkdir.ret) ? true : false;
-       return ret_value->u.mkdir.ret;
+       ret_value->_error = (ret_value->u.mkdirat.ret) ? true : false;
+       lttng_directory_handle_fini(&handle);
+       return ret_value->u.mkdirat.ret;
 }
 
 static
@@ -218,7 +234,7 @@ int _open(struct run_as_data *data, struct run_as_ret *ret_value)
        ret_value->u.open.ret = open(data->u.open.path, data->u.open.flags, data->u.open.mode);
        ret_value->fd = ret_value->u.open.ret;
        ret_value->_errno = errno;
-       ret_value->_error = (ret_value->u.open.ret) ? true : false;
+       ret_value->_error = ret_value->u.open.ret < 0;
        return ret_value->u.open.ret;
 }
 
@@ -240,6 +256,7 @@ int _rmdir_recursive(struct run_as_data *data, struct run_as_ret *ret_value)
        return ret_value->u.rmdir_recursive.ret;
 }
 
+#ifdef HAVE_ELF_H
 static
 int _extract_elf_symbol_offset(struct run_as_data *data,
                struct run_as_ret *ret_value)
@@ -298,21 +315,40 @@ free_offset:
 end:
        return ret;
 }
+#else
+static
+int _extract_elf_symbol_offset(struct run_as_data *data,
+               struct run_as_ret *ret_value)
+{
+       ERR("Unimplemented runas command RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET");
+       return -1;
+}
+
+static
+int _extract_sdt_probe_offsets(struct run_as_data *data,
+               struct run_as_ret *ret_value)
+{
+       ERR("Unimplemented runas command RUN_AS_EXTRACT_SDT_PROBE_OFFSETS");
+       return -1;
+}
+#endif
 
 static
 run_as_fct run_as_enum_to_fct(enum run_as_cmd cmd)
 {
        switch (cmd) {
        case RUN_AS_MKDIR:
-               return _mkdir;
+       case RUN_AS_MKDIRAT:
+               return _mkdirat;
+       case RUN_AS_MKDIR_RECURSIVE:
+       case RUN_AS_MKDIRAT_RECURSIVE:
+               return _mkdirat_recursive;
        case RUN_AS_OPEN:
                return _open;
        case RUN_AS_UNLINK:
                return _unlink;
        case RUN_AS_RMDIR_RECURSIVE:
                return _rmdir_recursive;
-       case RUN_AS_MKDIR_RECURSIVE:
-               return _mkdir_recursive;
        case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
                return _extract_elf_symbol_offset;
        case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
@@ -329,7 +365,8 @@ int do_send_fd(int sock, int fd)
        ssize_t len;
 
        if (fd < 0) {
-               ERR("Invalid file description");
+               ERR("Attempt to send invalid file descriptor to master (fd = %i)", fd);
+               /* Return 0 as this is not a fatal error. */
                return 0;
        }
 
@@ -346,11 +383,6 @@ int do_recv_fd(int sock, int *fd)
 {
        ssize_t len;
 
-       if (*fd < 0) {
-               ERR("Invalid file description");
-               return 0;
-       }
-
        len = lttcomm_recv_fds_unix_sock(sock, fd, 1);
 
        if (!len) {
@@ -359,6 +391,12 @@ int do_recv_fd(int sock, int *fd)
                PERROR("lttcomm_recv_fds_unix_sock");
                return -1;
        }
+       if (*fd < 0) {
+               ERR("Invalid file descriptor received from worker (fd = %i)", *fd);
+               /* Return 0 as this is not a fatal error. */
+               return 0;
+       }
+
        return 0;
 }
 
@@ -370,11 +408,18 @@ int send_fd_to_worker(struct run_as_worker *worker, enum run_as_cmd cmd, int fd)
        switch (cmd) {
        case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
        case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+       case RUN_AS_MKDIRAT:
+       case RUN_AS_MKDIRAT_RECURSIVE:
                break;
        default:
                return 0;
        }
 
+       if (fd < 0) {
+               ERR("Refusing to send invalid fd to worker (fd = %i)", fd);
+               return -1;
+       }
+
        ret = do_send_fd(worker->sockpair[0], fd);
        if (ret < 0) {
                PERROR("do_send_fd");
@@ -396,20 +441,21 @@ int send_fd_to_master(struct run_as_worker *worker, enum run_as_cmd cmd, int fd)
                return 0;
        }
 
+       if (fd < 0) {
+               DBG("Not sending file descriptor to master as it is invalid (fd = %i)", fd);
+               return 0;
+       }
        ret = do_send_fd(worker->sockpair[1], fd);
        if (ret < 0) {
                PERROR("do_send_fd error");
                ret = -1;
        }
 
-       if (fd < 0) {
-               goto end;
-       }
        ret_close = close(fd);
        if (ret_close < 0) {
                PERROR("close");
        }
-end:
+
        return ret;
 }
 
@@ -442,7 +488,13 @@ int recv_fd_from_master(struct run_as_worker *worker, enum run_as_cmd cmd, int *
        switch (cmd) {
        case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
        case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+       case RUN_AS_MKDIRAT:
+       case RUN_AS_MKDIRAT_RECURSIVE:
                break;
+       case RUN_AS_MKDIR:
+       case RUN_AS_MKDIR_RECURSIVE:
+               *fd = AT_FDCWD;
+               /* fall-through */
        default:
                return 0;
        }
@@ -464,6 +516,8 @@ int cleanup_received_fd(enum run_as_cmd cmd, int fd)
        switch (cmd) {
        case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
        case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+       case RUN_AS_MKDIRAT:
+       case RUN_AS_MKDIRAT_RECURSIVE:
                break;
        default:
                return 0;
@@ -726,6 +780,11 @@ int run_as_cmd(struct run_as_worker *worker,
                goto end;
        }
 
+       if (ret_value->_error) {
+               /* Skip stage 5 on error as there will be no fd to receive. */
+               goto end;
+       }
+
        /*
         * Stage 5: Receive file descriptor if needed
         */
@@ -768,363 +827,146 @@ end:
 }
 
 static
-int run_as_restart_worker(struct run_as_worker *worker)
+int reset_sighandler(void)
 {
-       int ret = 0;
-       char *procname = NULL;
-
-       procname = worker->procname;
-
-       /* Close socket to run_as worker process and clean up the zombie process */
-       run_as_destroy_worker();
+       int sig;
 
-       /* Create a new run_as worker process*/
-       ret = run_as_create_worker(procname);
-       if (ret < 0 ) {
-               ERR("Restarting the worker process failed");
-               ret = -1;
-               goto err;
+       DBG("Resetting run_as worker signal handlers to default");
+       for (sig = 1; sig <= 31; sig++) {
+               (void) signal(sig, SIG_DFL);
        }
-err:
-       return ret;
+       return 0;
 }
 
 static
-int run_as(enum run_as_cmd cmd, struct run_as_data *data,
-                  struct run_as_ret *ret_value, uid_t uid, gid_t gid)
+void worker_sighandler(int sig)
 {
-       int ret, saved_errno;
-
-       if (use_clone()) {
-               DBG("Using run_as worker");
-               pthread_mutex_lock(&worker_lock);
-               assert(global_worker);
+       const char *signame;
 
-               ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid);
-               saved_errno = ret_value->_errno;
+       /*
+        * The worker will inherit its parent's signals since they are part of
+        * the same process group. However, in the case of SIGINT and SIGTERM,
+        * we want to give the worker a chance to teardown gracefully when its
+        * parent closes the command socket.
+        */
+       switch (sig) {
+       case SIGINT:
+               signame = "SIGINT";
+               break;
+       case SIGTERM:
+               signame = "SIGTERM";
+               break;
+       default:
+               signame = NULL;
+       }
 
-               pthread_mutex_unlock(&worker_lock);
-               /*
-                * If the worker thread crashed the errno is set to EIO. we log
-                * the error and  start a new worker process.
-                */
-               if (ret == -1 && saved_errno == EIO) {
-                       DBG("Socket closed unexpectedly... "
-                                       "Restarting the worker process");
-                       ret = run_as_restart_worker(global_worker);
-                       if (ret == -1) {
-                               ERR("Failed to restart worker process.");
-                               goto err;
-                       }
-               }
+       if (signame) {
+               DBG("run_as worker received signal %s", signame);
        } else {
-               DBG("Using run_as without worker");
-               ret = run_as_noworker(cmd, data, ret_value, uid, gid);
+               DBG("run_as_worker received signal %d", sig);
        }
-err:
-       return ret;
 }
 
-LTTNG_HIDDEN
-int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
+static
+int set_worker_sighandlers(void)
 {
-       struct run_as_data data;
-       struct run_as_ret ret;
-
-       memset(&data, 0, sizeof(data));
-       memset(&ret, 0, sizeof(ret));
-       DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d",
-                       path, (int) mode, (int) uid, (int) gid);
-       strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
-       data.u.mkdir.path[PATH_MAX - 1] = '\0';
-       data.u.mkdir.mode = mode;
+       int ret = 0;
+       sigset_t sigset;
+       struct sigaction sa;
 
-       run_as(RUN_AS_MKDIR_RECURSIVE, &data, &ret, uid, gid);
-       errno = ret._errno;
-       return ret.u.mkdir.ret;
-}
+       if ((ret = sigemptyset(&sigset)) < 0) {
+               PERROR("sigemptyset");
+               goto end;
+       }
 
-LTTNG_HIDDEN
-int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
-{
-       struct run_as_data data;
-       struct run_as_ret ret;
+       sa.sa_handler = worker_sighandler;
+       sa.sa_mask = sigset;
+       sa.sa_flags = 0;
+       if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
+               PERROR("sigaction SIGINT");
+               goto end;
+       }
 
-       memset(&data, 0, sizeof(data));
-       memset(&ret, 0, sizeof(ret));
+       if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
+               PERROR("sigaction SIGTERM");
+               goto end;
+       }
 
-       DBG3("mkdir() %s with mode %d for uid %d and gid %d",
-                       path, (int) mode, (int) uid, (int) gid);
-       strncpy(data.u.mkdir.path, path, PATH_MAX - 1);
-       data.u.mkdir.path[PATH_MAX - 1] = '\0';
-       data.u.mkdir.mode = mode;
-       run_as(RUN_AS_MKDIR, &data, &ret, uid, gid);
-       errno = ret._errno;
-       return ret.u.mkdir.ret;
+       DBG("run_as signal handler set for SIGTERM and SIGINT");
+end:
+       return ret;
 }
 
-LTTNG_HIDDEN
-int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
+static
+int run_as_create_worker_no_lock(const char *procname,
+               post_fork_cleanup_cb clean_up_func,
+               void *clean_up_user_data)
 {
-       struct run_as_data data;
-       struct run_as_ret ret;
-
-       memset(&data, 0, sizeof(data));
-       memset(&ret, 0, sizeof(ret));
+       pid_t pid;
+       int i, ret = 0;
+       ssize_t readlen;
+       struct run_as_ret recvret;
+       struct run_as_worker *worker;
 
-       DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
-                       path, flags, (int) mode, (int) uid, (int) gid);
-       strncpy(data.u.open.path, path, PATH_MAX - 1);
-       data.u.open.path[PATH_MAX - 1] = '\0';
-       data.u.open.flags = flags;
-       data.u.open.mode = mode;
-       run_as(RUN_AS_OPEN, &data, &ret, uid, gid);
-       errno = ret._errno;
-       ret.u.open.ret = ret.fd;
-       return ret.u.open.ret;
-}
+       assert(!global_worker);
+       if (!use_clone()) {
+               /*
+                * Don't initialize a worker, all run_as tasks will be performed
+                * in the current process.
+                */
+               ret = 0;
+               goto end;
+       }
+       worker = zmalloc(sizeof(*worker));
+       if (!worker) {
+               ret = -ENOMEM;
+               goto end;
+       }
+       worker->procname = strdup(procname);
+       if (!worker->procname) {
+               ret = -ENOMEM;
+               goto error_procname_alloc;
+       }
+       /* Create unix socket. */
+       if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
+               ret = -1;
+               goto error_sock;
+       }
 
-LTTNG_HIDDEN
-int run_as_unlink(const char *path, uid_t uid, gid_t gid)
-{
-       struct run_as_data data;
-       struct run_as_ret ret;
+       /* Fork worker. */
+       pid = fork();
+       if (pid < 0) {
+               PERROR("fork");
+               ret = -1;
+               goto error_fork;
+       } else if (pid == 0) {
+               /* Child */
 
-       memset(&data, 0, sizeof(data));
-       memset(&ret, 0, sizeof(ret));
+               reset_sighandler();
 
-       DBG3("unlink() %s with for uid %d and gid %d",
-                       path, (int) uid, (int) gid);
-       strncpy(data.u.unlink.path, path, PATH_MAX - 1);
-       data.u.unlink.path[PATH_MAX - 1] = '\0';
-       run_as(RUN_AS_UNLINK, &data, &ret, uid, gid);
-       errno = ret._errno;
-       return ret.u.unlink.ret;
-}
+               set_worker_sighandlers();
+               if (clean_up_func) {
+                       if (clean_up_func(clean_up_user_data) < 0) {
+                               ERR("Run-as post-fork clean-up failed, exiting.");
+                               exit(EXIT_FAILURE);
+                       }
+               }
 
-LTTNG_HIDDEN
-int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
-{
-       struct run_as_data data;
-       struct run_as_ret ret;
+               /* Just close, no shutdown. */
+               if (close(worker->sockpair[0])) {
+                       PERROR("close");
+                       exit(EXIT_FAILURE);
+               }
 
-       memset(&data, 0, sizeof(data));
-       memset(&ret, 0, sizeof(ret));
-
-       DBG3("rmdir_recursive() %s with for uid %d and gid %d",
-                       path, (int) uid, (int) gid);
-       strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1);
-       data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0';
-       run_as(RUN_AS_RMDIR_RECURSIVE, &data, &ret, uid, gid);
-       errno = ret._errno;
-       return ret.u.rmdir_recursive.ret;
-}
-
-LTTNG_HIDDEN
-int run_as_extract_elf_symbol_offset(int fd, const char* function,
-               uid_t uid, gid_t gid, uint64_t *offset)
-{
-       struct run_as_data data;
-       struct run_as_ret ret;
-
-       memset(&data, 0, sizeof(data));
-       memset(&ret, 0, sizeof(ret));
-
-       DBG3("extract_elf_symbol_offset() on fd=%d and function=%s "
-               "with for uid %d and gid %d", fd, function, (int) uid, (int) gid);
-
-       data.fd = fd;
-
-       strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1);
-
-       data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
-
-       run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &ret, uid, gid);
-
-       errno = ret._errno;
-
-       if (ret._error) {
-               return -1;
-       }
-
-       *offset = ret.u.extract_elf_symbol_offset.offset;
-       return 0;
-}
-
-LTTNG_HIDDEN
-int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name,
-               const char* probe_name, uid_t uid, gid_t gid,
-               uint64_t **offsets, uint32_t *num_offset)
-{
-       struct run_as_data data;
-       struct run_as_ret ret;
-
-       memset(&data, 0, sizeof(data));
-       memset(&ret, 0, sizeof(ret));
-
-       DBG3("extract_sdt_probe_offsets() on fd=%d, probe_name=%s and "
-               "provider_name=%s with for uid %d and gid %d", fd, probe_name,
-               provider_name, (int) uid, (int) gid);
-
-       data.fd = fd;
-
-       strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name, LTTNG_SYMBOL_NAME_LEN - 1);
-       strncpy(data.u.extract_sdt_probe_offsets.provider_name, provider_name, LTTNG_SYMBOL_NAME_LEN - 1);
-
-       data.u.extract_sdt_probe_offsets.probe_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
-       data.u.extract_sdt_probe_offsets.provider_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
-
-       run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &ret, uid, gid);
-
-       errno = ret._errno;
-
-       if (ret._error) {
-               return -1;
-       }
-
-       *num_offset = ret.u.extract_sdt_probe_offsets.num_offset;
-
-       *offsets = zmalloc(*num_offset * sizeof(uint64_t));
-       if (!*offsets) {
-               return -ENOMEM;
-       }
-
-       memcpy(*offsets, ret.u.extract_sdt_probe_offsets.offsets, *num_offset * sizeof(uint64_t));
-       return 0;
-}
-
-static
-int reset_sighandler(void)
-{
-       int sig;
-
-       DBG("Resetting run_as worker signal handlers to default");
-       for (sig = 1; sig <= 31; sig++) {
-               (void) signal(sig, SIG_DFL);
-       }
-       return 0;
-}
-
-static
-void worker_sighandler(int sig)
-{
-       const char *signame;
-
-       /*
-        * The worker will inherit its parent's signals since they are part of
-        * the same process group. However, in the case of SIGINT and SIGTERM,
-        * we want to give the worker a chance to teardown gracefully when its
-        * parent closes the command socket.
-        */
-       switch (sig) {
-       case SIGINT:
-               signame = "SIGINT";
-               break;
-       case SIGTERM:
-               signame = "SIGTERM";
-               break;
-       default:
-               signame = NULL;
-       }
-
-       if (signame) {
-               DBG("run_as worker received signal %s", signame);
-       } else {
-               DBG("run_as_worker received signal %d", sig);
-       }
-}
-
-static
-int set_worker_sighandlers(void)
-{
-       int ret = 0;
-       sigset_t sigset;
-       struct sigaction sa;
-
-       if ((ret = sigemptyset(&sigset)) < 0) {
-               PERROR("sigemptyset");
-               goto end;
-       }
-
-       sa.sa_handler = worker_sighandler;
-       sa.sa_mask = sigset;
-       sa.sa_flags = 0;
-       if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) {
-               PERROR("sigaction SIGINT");
-               goto end;
-       }
-
-       if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
-               PERROR("sigaction SIGTERM");
-               goto end;
-       }
-
-       DBG("run_as signal handler set for SIGTERM and SIGINT");
-end:
-       return ret;
-}
-
-LTTNG_HIDDEN
-int run_as_create_worker(char *procname)
-{
-       pid_t pid;
-       int i, ret = 0;
-       ssize_t readlen;
-       struct run_as_ret recvret;
-       struct run_as_worker *worker;
-
-       pthread_mutex_lock(&worker_lock);
-       assert(!global_worker);
-       if (!use_clone()) {
-               /*
-                * Don't initialize a worker, all run_as tasks will be performed
-                * in the current process.
-                */
-               ret = 0;
-               goto end;
-       }
-       worker = zmalloc(sizeof(*worker));
-       if (!worker) {
-               ret = -ENOMEM;
-               goto end;
-       }
-       worker->procname = procname;
-       /* Create unix socket. */
-       if (lttcomm_create_anon_unix_socketpair(worker->sockpair) < 0) {
-               ret = -1;
-               goto error_sock;
-       }
-
-       /* Fork worker. */
-       pid = fork();
-       if (pid < 0) {
-               PERROR("fork");
-               ret = -1;
-               goto error_fork;
-       } else if (pid == 0) {
-               /* Child */
-
-               reset_sighandler();
-
-               set_worker_sighandlers();
-
-               /* The child has no use for this lock. */
-               pthread_mutex_unlock(&worker_lock);
-               /* Just close, no shutdown. */
-               if (close(worker->sockpair[0])) {
-                       PERROR("close");
-                       exit(EXIT_FAILURE);
-               }
-
-               /*
-                * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1]
-                * Sockpair[1] is used as a control channel with the master
-                */
-               for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
-                       if (i != worker->sockpair[1]) {
-                               (void) close(i);
-                       }
-               }
+               /*
+                * Close all FDs aside from STDIN, STDOUT, STDERR and sockpair[1]
+                * Sockpair[1] is used as a control channel with the master
+                */
+               for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) {
+                       if (i != worker->sockpair[1]) {
+                               (void) close(i);
+                       }
+               }
 
                worker->sockpair[0] = -1;
                ret = run_as_worker(worker);
@@ -1133,6 +975,8 @@ int run_as_create_worker(char *procname)
                        ret = -1;
                }
                worker->sockpair[1] = -1;
+               free(worker->procname);
+               free(worker);
                LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
                exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
        } else {
@@ -1158,7 +1002,6 @@ int run_as_create_worker(char *procname)
                global_worker = worker;
        }
 end:
-       pthread_mutex_unlock(&worker_lock);
        return ret;
 
        /* Error handling. */
@@ -1173,20 +1016,20 @@ error_fork:
                worker->sockpair[i] = -1;
        }
 error_sock:
+       free(worker->procname);
+error_procname_alloc:
        free(worker);
-       pthread_mutex_unlock(&worker_lock);
        return ret;
 }
 
-LTTNG_HIDDEN
-void run_as_destroy_worker(void)
+static
+void run_as_destroy_worker_no_lock(void)
 {
        struct run_as_worker *worker = global_worker;
 
        DBG("Destroying run_as worker");
-       pthread_mutex_lock(&worker_lock);
        if (!worker) {
-               goto end;
+               return;
        }
        /* Close unix socket */
        DBG("Closing run_as worker socket");
@@ -1219,8 +1062,291 @@ void run_as_destroy_worker(void)
                        break;
                }
        }
+       free(worker->procname);
        free(worker);
        global_worker = NULL;
-end:
+}
+
+static
+int run_as_restart_worker(struct run_as_worker *worker)
+{
+       int ret = 0;
+       char *procname = NULL;
+
+       procname = worker->procname;
+
+       /* Close socket to run_as worker process and clean up the zombie process */
+       run_as_destroy_worker_no_lock();
+
+       /* Create a new run_as worker process*/
+       ret = run_as_create_worker_no_lock(procname, NULL, NULL);
+       if (ret < 0 ) {
+               ERR("Restarting the worker process failed");
+               ret = -1;
+               goto err;
+       }
+err:
+       return ret;
+}
+
+static
+int run_as(enum run_as_cmd cmd, struct run_as_data *data,
+                  struct run_as_ret *ret_value, uid_t uid, gid_t gid)
+{
+       int ret, saved_errno;
+
+       pthread_mutex_lock(&worker_lock);
+       if (use_clone()) {
+               DBG("Using run_as worker");
+
+               assert(global_worker);
+
+               ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid);
+               saved_errno = ret_value->_errno;
+
+               /*
+                * If the worker thread crashed the errno is set to EIO. we log
+                * the error and  start a new worker process.
+                */
+               if (ret == -1 && saved_errno == EIO) {
+                       DBG("Socket closed unexpectedly... "
+                                       "Restarting the worker process");
+                       ret = run_as_restart_worker(global_worker);
+                       if (ret == -1) {
+                               ERR("Failed to restart worker process.");
+                               goto err;
+                       }
+               }
+       } else {
+               DBG("Using run_as without worker");
+               ret = run_as_noworker(cmd, data, ret_value, uid, gid);
+       }
+err:
+       pthread_mutex_unlock(&worker_lock);
+       return ret;
+}
+
+LTTNG_HIDDEN
+int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       return run_as_mkdirat_recursive(AT_FDCWD, path, mode, uid, gid);
+}
+
+LTTNG_HIDDEN
+int run_as_mkdirat_recursive(int dirfd, const char *path, mode_t mode,
+               uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data;
+       struct run_as_ret run_as_ret;
+
+       memset(&data, 0, sizeof(data));
+       memset(&run_as_ret, 0, sizeof(run_as_ret));
+       DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, (int) mode, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.mkdirat.path, path,
+                       sizeof(data.u.mkdirat.path));
+       if (ret) {
+               ERR("Failed to copy path argument of mkdirat recursive command");
+               goto error;
+       }
+       data.u.mkdirat.path[PATH_MAX - 1] = '\0';
+       data.u.mkdirat.mode = mode;
+       data.fd = dirfd;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR_RECURSIVE : RUN_AS_MKDIRAT_RECURSIVE,
+                       &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret.u.mkdirat.ret;
+error:
+       return ret;
+}
+
+LTTNG_HIDDEN
+int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       return run_as_mkdirat(AT_FDCWD, path, mode, uid, gid);
+}
+
+LTTNG_HIDDEN
+int run_as_mkdirat(int dirfd, const char *path, mode_t mode,
+               uid_t uid, gid_t gid)
+{
+       int ret;
+       struct run_as_data data;
+       struct run_as_ret run_as_ret;
+
+       memset(&data, 0, sizeof(data));
+       memset(&run_as_ret, 0, sizeof(run_as_ret));
+
+       DBG3("mkdirat() recursive fd = %d%s, path = %s, mode = %d, uid = %d, gid = %d",
+                       dirfd, dirfd == AT_FDCWD ? " (AT_FDCWD)" : "",
+                       path, (int) mode, (int) uid, (int) gid);
+       ret = lttng_strncpy(data.u.mkdirat.path, path,
+                       sizeof(data.u.mkdirat.path));
+       if (ret) {
+               ERR("Failed to copy path argument of mkdirat command");
+               goto error;
+       }
+       data.u.mkdirat.path[PATH_MAX - 1] = '\0';
+       data.u.mkdirat.mode = mode;
+       data.fd = dirfd;
+       run_as(dirfd == AT_FDCWD ? RUN_AS_MKDIR : RUN_AS_MKDIRAT,
+                       &data, &run_as_ret, uid, gid);
+       errno = run_as_ret._errno;
+       ret = run_as_ret._errno;
+error:
+       return ret;
+}
+
+LTTNG_HIDDEN
+int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid)
+{
+       struct run_as_data data;
+       struct run_as_ret ret;
+
+       memset(&data, 0, sizeof(data));
+       memset(&ret, 0, sizeof(ret));
+
+       DBG3("open() %s with flags %X mode %d for uid %d and gid %d",
+                       path, flags, (int) mode, (int) uid, (int) gid);
+       strncpy(data.u.open.path, path, PATH_MAX - 1);
+       data.u.open.path[PATH_MAX - 1] = '\0';
+       data.u.open.flags = flags;
+       data.u.open.mode = mode;
+       run_as(RUN_AS_OPEN, &data, &ret, uid, gid);
+       errno = ret._errno;
+       ret.u.open.ret = ret.fd;
+       return ret.u.open.ret;
+}
+
+LTTNG_HIDDEN
+int run_as_unlink(const char *path, uid_t uid, gid_t gid)
+{
+       struct run_as_data data;
+       struct run_as_ret ret;
+
+       memset(&data, 0, sizeof(data));
+       memset(&ret, 0, sizeof(ret));
+
+       DBG3("unlink() %s with for uid %d and gid %d",
+                       path, (int) uid, (int) gid);
+       strncpy(data.u.unlink.path, path, PATH_MAX - 1);
+       data.u.unlink.path[PATH_MAX - 1] = '\0';
+       run_as(RUN_AS_UNLINK, &data, &ret, uid, gid);
+       errno = ret._errno;
+       return ret.u.unlink.ret;
+}
+
+LTTNG_HIDDEN
+int run_as_rmdir_recursive(const char *path, uid_t uid, gid_t gid)
+{
+       struct run_as_data data;
+       struct run_as_ret ret;
+
+       memset(&data, 0, sizeof(data));
+       memset(&ret, 0, sizeof(ret));
+
+       DBG3("rmdir_recursive() %s with for uid %d and gid %d",
+                       path, (int) uid, (int) gid);
+       strncpy(data.u.rmdir_recursive.path, path, PATH_MAX - 1);
+       data.u.rmdir_recursive.path[PATH_MAX - 1] = '\0';
+       run_as(RUN_AS_RMDIR_RECURSIVE, &data, &ret, uid, gid);
+       errno = ret._errno;
+       return ret.u.rmdir_recursive.ret;
+}
+
+LTTNG_HIDDEN
+int run_as_extract_elf_symbol_offset(int fd, const char* function,
+               uid_t uid, gid_t gid, uint64_t *offset)
+{
+       struct run_as_data data;
+       struct run_as_ret ret;
+
+       memset(&data, 0, sizeof(data));
+       memset(&ret, 0, sizeof(ret));
+
+       DBG3("extract_elf_symbol_offset() on fd=%d and function=%s "
+               "with for uid %d and gid %d", fd, function, (int) uid, (int) gid);
+
+       data.fd = fd;
+
+       strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1);
+
+       data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
+
+       run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &ret, uid, gid);
+
+       errno = ret._errno;
+
+       if (ret._error) {
+               return -1;
+       }
+
+       *offset = ret.u.extract_elf_symbol_offset.offset;
+       return 0;
+}
+
+LTTNG_HIDDEN
+int run_as_extract_sdt_probe_offsets(int fd, const char* provider_name,
+               const char* probe_name, uid_t uid, gid_t gid,
+               uint64_t **offsets, uint32_t *num_offset)
+{
+       struct run_as_data data;
+       struct run_as_ret ret;
+
+       memset(&data, 0, sizeof(data));
+       memset(&ret, 0, sizeof(ret));
+
+       DBG3("extract_sdt_probe_offsets() on fd=%d, probe_name=%s and "
+               "provider_name=%s with for uid %d and gid %d", fd, probe_name,
+               provider_name, (int) uid, (int) gid);
+
+       data.fd = fd;
+
+       strncpy(data.u.extract_sdt_probe_offsets.probe_name, probe_name, LTTNG_SYMBOL_NAME_LEN - 1);
+       strncpy(data.u.extract_sdt_probe_offsets.provider_name, provider_name, LTTNG_SYMBOL_NAME_LEN - 1);
+
+       data.u.extract_sdt_probe_offsets.probe_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
+       data.u.extract_sdt_probe_offsets.provider_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
+
+       run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &ret, uid, gid);
+
+       errno = ret._errno;
+
+       if (ret._error) {
+               return -1;
+       }
+
+       *num_offset = ret.u.extract_sdt_probe_offsets.num_offset;
+
+       *offsets = zmalloc(*num_offset * sizeof(uint64_t));
+       if (!*offsets) {
+               return -ENOMEM;
+       }
+
+       memcpy(*offsets, ret.u.extract_sdt_probe_offsets.offsets, *num_offset * sizeof(uint64_t));
+       return 0;
+}
+
+LTTNG_HIDDEN
+int run_as_create_worker(const char *procname,
+               post_fork_cleanup_cb clean_up_func,
+               void *clean_up_user_data)
+{
+       int ret;
+
+       pthread_mutex_lock(&worker_lock);
+       ret = run_as_create_worker_no_lock(procname, clean_up_func,
+                       clean_up_user_data);
+       pthread_mutex_unlock(&worker_lock);
+       return ret;
+}
+
+LTTNG_HIDDEN
+void run_as_destroy_worker(void)
+{
+       pthread_mutex_lock(&worker_lock);
+       run_as_destroy_worker_no_lock();
        pthread_mutex_unlock(&worker_lock);
 }
This page took 0.036487 seconds and 5 git commands to generate.