#include <assert.h>
#include <signal.h>
+#include <common/lttng-kernel.h>
#include <common/common.h>
#include <common/utils.h>
#include <common/compat/getenv.h>
#include <common/compat/prctl.h>
#include <common/unix.h>
#include <common/defaults.h>
+#include <common/lttng-elf.h>
+
+#include <lttng/constant.h>
#include "runas.h"
char path[PATH_MAX];
};
+struct run_as_extract_elf_symbol_offset_data {
+ char function[LTTNG_SYMBOL_NAME_LEN];
+};
+
+struct run_as_extract_sdt_probe_offsets_data {
+ char probe_name[LTTNG_SYMBOL_NAME_LEN];
+ char provider_name[LTTNG_SYMBOL_NAME_LEN];
+};
+
struct run_as_mkdir_ret {
int ret;
};
int ret;
};
+struct run_as_extract_elf_symbol_offset_ret {
+ uint64_t offset;
+};
+
+struct run_as_extract_sdt_probe_offsets_ret {
+ uint32_t num_offset;
+ uint64_t offsets[LTTNG_KERNEL_MAX_UPROBE_NUM];
+};
+
enum run_as_cmd {
RUN_AS_MKDIR,
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,
};
struct run_as_data {
struct run_as_open_data open;
struct run_as_unlink_data unlink;
struct run_as_rmdir_recursive_data rmdir_recursive;
+ struct run_as_extract_elf_symbol_offset_data extract_elf_symbol_offset;
+ struct run_as_extract_sdt_probe_offsets_data extract_sdt_probe_offsets;
} u;
uid_t uid;
gid_t gid;
struct run_as_open_ret open;
struct run_as_unlink_ret unlink;
struct run_as_rmdir_recursive_ret rmdir_recursive;
+ struct run_as_extract_elf_symbol_offset_ret extract_elf_symbol_offset;
+ struct run_as_extract_sdt_probe_offsets_ret extract_sdt_probe_offsets;
} u;
int _errno;
bool _error;
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;
}
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)
+{
+ int ret = 0;
+ ret_value->_error = false;
+
+ ret = lttng_elf_get_symbol_offset(data->fd,
+ data->u.extract_elf_symbol_offset.function,
+ &ret_value->u.extract_elf_symbol_offset.offset);
+ if (ret) {
+ DBG("Failed to extract ELF function offset");
+ ret_value->_error = true;
+ }
+
+ return ret;
+}
+
+static
+int _extract_sdt_probe_offsets(struct run_as_data *data,
+ struct run_as_ret *ret_value)
+{
+ int ret = 0;
+ uint64_t *offsets = NULL;
+ uint32_t num_offset;
+
+ ret_value->_error = false;
+
+ /* On success, this call allocates the offsets paramater. */
+ ret = lttng_elf_get_sdt_probe_offsets(data->fd,
+ data->u.extract_sdt_probe_offsets.provider_name,
+ data->u.extract_sdt_probe_offsets.probe_name,
+ &offsets, &num_offset);
+
+ if (ret) {
+ DBG("Failed to extract SDT probe offsets");
+ ret_value->_error = true;
+ goto end;
+ }
+
+ if (num_offset <= 0 || num_offset > LTTNG_KERNEL_MAX_UPROBE_NUM) {
+ DBG("Wrong number of probes.");
+ ret = -1;
+ ret_value->_error = true;
+ goto free_offset;
+ }
+
+ /* Copy the content of the offsets array to the ret struct. */
+ memcpy(ret_value->u.extract_sdt_probe_offsets.offsets,
+ offsets, num_offset * sizeof(uint64_t));
+
+ ret_value->u.extract_sdt_probe_offsets.num_offset = num_offset;
+
+free_offset:
+ free(offsets);
+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)
{
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:
+ return _extract_sdt_probe_offsets;
default:
ERR("Unknown command %d", (int) cmd);
return NULL;
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;
}
{
ssize_t len;
- if (*fd < 0) {
- ERR("Invalid file description");
- return 0;
- }
-
len = lttcomm_recv_fds_unix_sock(sock, fd, 1);
if (!len) {
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;
}
int ret = 0;
switch (cmd) {
+ case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
+ case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+ 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");
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");
int ret = 0;
switch (cmd) {
+ case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
+ case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+ break;
default:
return 0;
}
int ret = 0;
switch (cmd) {
+ case RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET:
+ case RUN_AS_EXTRACT_SDT_PROBE_OFFSETS:
+ break;
default:
return 0;
}
+ if (fd < 0) {
+ return 0;
+ }
ret = close(fd);
if (ret < 0) {
PERROR("close error");
return ret;
}
+
/*
* Return < 0 on error, 0 if OK, 1 on hangup.
*/
run_as_fct cmd;
uid_t prev_euid;
+ memset(&sendret, 0, sizeof(sendret));
+ sendret.fd = -1;
+
/*
* Stage 1: Receive run_as_data struct from the master.
* The structure contains the command type and all the parameters needed for
if (data.gid != getegid()) {
ret = setegid(data.gid);
if (ret < 0) {
+ sendret._error = true;
+ sendret._errno = errno;
PERROR("setegid");
goto write_return;
}
if (data.uid != prev_euid) {
ret = seteuid(data.uid);
if (ret < 0) {
+ sendret._error = true;
+ sendret._errno = errno;
PERROR("seteuid");
goto write_return;
}
PERROR("Error reading response from run_as");
ret = -1;
ret_value->_errno = errno;
+ goto end;
+ }
+
+ if (ret_value->_error) {
+ /* Skip stage 5 on error as there will be no fd to receive. */
+ goto end;
}
/*
return ret;
}
+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;
+}
+
+static
+int run_as_create_worker_no_lock(const char *procname)
+{
+ pid_t pid;
+ int i, ret = 0;
+ ssize_t readlen;
+ struct run_as_ret recvret;
+ struct run_as_worker *worker;
+
+ 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;
+ }
+
+ /* Fork worker. */
+ pid = fork();
+ if (pid < 0) {
+ PERROR("fork");
+ ret = -1;
+ goto error_fork;
+ } else if (pid == 0) {
+ /* Child */
+
+ reset_sighandler();
+
+ set_worker_sighandlers();
+
+ /* 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);
+ }
+ }
+
+ worker->sockpair[0] = -1;
+ ret = run_as_worker(worker);
+ if (lttcomm_close_unix_sock(worker->sockpair[1])) {
+ PERROR("close");
+ 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 {
+ /* Parent */
+
+ /* Just close, no shutdown. */
+ if (close(worker->sockpair[1])) {
+ PERROR("close");
+ ret = -1;
+ goto error_fork;
+ }
+ worker->sockpair[1] = -1;
+ worker->pid = pid;
+ /* Wait for worker to become ready. */
+ readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
+ &recvret, sizeof(recvret));
+ if (readlen < sizeof(recvret)) {
+ ERR("readlen: %zd", readlen);
+ PERROR("Error reading response from run_as at creation");
+ ret = -1;
+ goto error_fork;
+ }
+ global_worker = worker;
+ }
+end:
+ return ret;
+
+ /* Error handling. */
+error_fork:
+ for (i = 0; i < 2; i++) {
+ if (worker->sockpair[i] < 0) {
+ continue;
+ }
+ if (lttcomm_close_unix_sock(worker->sockpair[i])) {
+ PERROR("close");
+ }
+ worker->sockpair[i] = -1;
+ }
+error_sock:
+ free(worker->procname);
+error_procname_alloc:
+ free(worker);
+ return ret;
+}
+
static
int run_as_restart_worker(struct run_as_worker *worker)
{
run_as_destroy_worker();
/* Create a new run_as worker process*/
- ret = run_as_create_worker(procname);
+ ret = run_as_create_worker_no_lock(procname);
if (ret < 0 ) {
ERR("Restarting the worker process failed");
ret = -1;
{
int ret, saved_errno;
+ pthread_mutex_lock(&worker_lock);
if (use_clone()) {
DBG("Using run_as worker");
- pthread_mutex_lock(&worker_lock);
+
assert(global_worker);
ret = run_as_cmd(global_worker, cmd, data, ret_value, uid, gid);
saved_errno = ret_value->_errno;
- 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.
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;
ret = run_as_noworker(cmd, data, ret_value, uid, gid);
}
err:
+ pthread_mutex_unlock(&worker_lock);
return ret;
}
return ret.u.rmdir_recursive.ret;
}
-static
-int reset_sighandler(void)
+LTTNG_HIDDEN
+int run_as_extract_elf_symbol_offset(int fd, const char* function,
+ uid_t uid, gid_t gid, uint64_t *offset)
{
- int sig;
+ struct run_as_data data;
+ struct run_as_ret ret;
- DBG("Resetting run_as worker signal handlers to default");
- for (sig = 1; sig <= 31; sig++) {
- (void) signal(sig, SIG_DFL);
- }
- return 0;
-}
+ memset(&data, 0, sizeof(data));
+ memset(&ret, 0, sizeof(ret));
-static
-void worker_sighandler(int sig)
-{
- const char *signame;
+ DBG3("extract_elf_symbol_offset() on fd=%d and function=%s "
+ "with for uid %d and gid %d", fd, function, (int) uid, (int) gid);
- /*
- * 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;
- }
+ data.fd = fd;
- if (signame) {
- DBG("run_as worker received signal %s", signame);
- } else {
- DBG("run_as_worker received signal %d", sig);
- }
-}
+ strncpy(data.u.extract_elf_symbol_offset.function, function, LTTNG_SYMBOL_NAME_LEN - 1);
-static
-int set_worker_sighandlers(void)
-{
- int ret = 0;
- sigset_t sigset;
- struct sigaction sa;
+ data.u.extract_elf_symbol_offset.function[LTTNG_SYMBOL_NAME_LEN - 1] = '\0';
- if ((ret = sigemptyset(&sigset)) < 0) {
- PERROR("sigemptyset");
- goto end;
- }
+ run_as(RUN_AS_EXTRACT_ELF_SYMBOL_OFFSET, &data, &ret, uid, gid);
- 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;
- }
+ errno = ret._errno;
- if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
- PERROR("sigaction SIGTERM");
- goto end;
+ if (ret._error) {
+ return -1;
}
- DBG("run_as signal handler set for SIGTERM and SIGINT");
-end:
- return ret;
+ *offset = ret.u.extract_elf_symbol_offset.offset;
+ return 0;
}
LTTNG_HIDDEN
-int run_as_create_worker(char *procname)
+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)
{
- pid_t pid;
- int i, ret = 0;
- ssize_t readlen;
- struct run_as_ret recvret;
- struct run_as_worker *worker;
+ struct run_as_data data;
+ struct run_as_ret ret;
- 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;
- }
+ memset(&data, 0, sizeof(data));
+ memset(&ret, 0, sizeof(ret));
- /* Fork worker. */
- pid = fork();
- if (pid < 0) {
- PERROR("fork");
- ret = -1;
- goto error_fork;
- } else if (pid == 0) {
- /* Child */
+ 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);
- reset_sighandler();
+ data.fd = fd;
- set_worker_sighandlers();
+ 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);
- /* 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);
- }
+ 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';
- /*
- * 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);
- }
- }
+ run_as(RUN_AS_EXTRACT_SDT_PROBE_OFFSETS, &data, &ret, uid, gid);
- worker->sockpair[0] = -1;
- ret = run_as_worker(worker);
- if (lttcomm_close_unix_sock(worker->sockpair[1])) {
- PERROR("close");
- ret = -1;
- }
- worker->sockpair[1] = -1;
- LOG(ret ? PRINT_ERR : PRINT_DBG, "run_as worker exiting (ret = %d)", ret);
- exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);
- } else {
- /* Parent */
+ errno = ret._errno;
- /* Just close, no shutdown. */
- if (close(worker->sockpair[1])) {
- PERROR("close");
- ret = -1;
- goto error_fork;
- }
- worker->sockpair[1] = -1;
- worker->pid = pid;
- /* Wait for worker to become ready. */
- readlen = lttcomm_recv_unix_sock(worker->sockpair[0],
- &recvret, sizeof(recvret));
- if (readlen < sizeof(recvret)) {
- ERR("readlen: %zd", readlen);
- PERROR("Error reading response from run_as at creation");
- ret = -1;
- goto error_fork;
- }
- global_worker = worker;
+ if (ret._error) {
+ return -1;
}
-end:
- pthread_mutex_unlock(&worker_lock);
- return ret;
- /* Error handling. */
-error_fork:
- for (i = 0; i < 2; i++) {
- if (worker->sockpair[i] < 0) {
- continue;
- }
- if (lttcomm_close_unix_sock(worker->sockpair[i])) {
- PERROR("close");
- }
- worker->sockpair[i] = -1;
+ *num_offset = ret.u.extract_sdt_probe_offsets.num_offset;
+
+ *offsets = zmalloc(*num_offset * sizeof(uint64_t));
+ if (!*offsets) {
+ return -ENOMEM;
}
-error_sock:
- free(worker);
+
+ 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)
+{
+ int ret;
+
+ pthread_mutex_lock(&worker_lock);
+ ret = run_as_create_worker_no_lock(procname);
pthread_mutex_unlock(&worker_lock);
return ret;
}
break;
}
}
+ free(worker->procname);
free(worker);
global_worker = NULL;
end: