static int run_as_cmd(struct run_as_worker *worker, enum run_as_cmd cmd, struct run_as_data *data, uid_t uid, gid_t gid) { ssize_t readlen, writelen; struct run_as_ret recvret; /* * If we are non-root, we can only deal with our own uid. */ if (geteuid() != 0) { if (uid != geteuid()) { recvret.ret = -1; recvret._errno = EPERM; ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)", (int) uid, (int) geteuid()); goto end; } } data->cmd = cmd; data->uid = uid; data->gid = gid; writelen = lttcomm_send_unix_sock(worker->sockpair[0], data, sizeof(*data)); if (writelen < sizeof(*data)) { PERROR("Error writing message to run_as"); recvret.ret = -1; recvret._errno = errno; goto end; } /* receive return value */ readlen = lttcomm_recv_unix_sock(worker->sockpair[0], &recvret, sizeof(recvret)); if (!readlen) { ERR("Run-as worker has hung-up during run_as_cmd"); recvret.ret = -1; recvret._errno = EIO; goto end; } else if (readlen < sizeof(recvret)) { PERROR("Error reading response from run_as"); recvret.ret = -1; recvret._errno = errno; } if (do_recv_fd(worker, cmd, &recvret.ret)) { recvret.ret = -1; recvret._errno = EIO; } end: errno = recvret._errno; return recvret.ret; }
/* * Send data on a unix socket using the liblttsessiondcomm API. * * Return lttcomm error code. */ static int send_unix_sock(int sock, void *buf, size_t len) { /* Check valid length */ if (len == 0) { return -1; } return lttcomm_send_unix_sock(sock, buf, len); }
static int run_as_worker(struct run_as_worker *worker) { int ret; ssize_t writelen; struct run_as_ret sendret; size_t proc_orig_len; /* * Initialize worker. Set a different process cmdline. */ proc_orig_len = strlen(worker->procname); memset(worker->procname, 0, proc_orig_len); strncpy(worker->procname, DEFAULT_RUN_AS_WORKER_NAME, proc_orig_len); ret = lttng_prctl(PR_SET_NAME, (unsigned long) DEFAULT_RUN_AS_WORKER_NAME, 0, 0, 0); if (ret && ret != -ENOSYS) { /* Don't fail as this is not essential. */ PERROR("prctl PR_SET_NAME"); ret = 0; } sendret.ret = 0; sendret._errno = 0; writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret, sizeof(sendret)); if (writelen < sizeof(sendret)) { PERROR("lttcomm_send_unix_sock error"); ret = EXIT_FAILURE; goto end; } for (;;) { ret = handle_one_cmd(worker); if (ret < 0) { ret = EXIT_FAILURE; goto end; } else if (ret > 0) { break; } else { continue; /* Next command. */ } } ret = EXIT_SUCCESS; end: return ret; }
/* * Send var len data to the session daemon. * * On success, returns the number of bytes sent (>=0) * On error, returns -1 */ static int send_session_varlen(void *data, size_t len) { int ret; if (!connected) { ret = -LTTNG_ERR_NO_SESSIOND; goto end; } if (!data || !len) { ret = 0; goto end; } ret = lttcomm_send_unix_sock(sessiond_socket, data, len); if (ret < 0) { ret = -LTTNG_ERR_FATAL; } end: return ret; }
/* * Check session daemon health for a specific health component. * * Return 0 if health is OK or else 1 if BAD. * * Any other negative value is a lttng error code which can be translated with * lttng_strerror(). */ int lttng_health_check(enum lttng_health_component c) { int sock, ret; struct lttcomm_health_msg msg; struct lttcomm_health_data reply; /* Connect to the sesssion daemon */ sock = lttcomm_connect_unix_sock(health_sock_path); if (sock < 0) { ret = -LTTNG_ERR_NO_SESSIOND; goto error; } msg.cmd = LTTNG_HEALTH_CHECK; msg.component = c; ret = lttcomm_send_unix_sock(sock, (void *)&msg, sizeof(msg)); if (ret < 0) { ret = -LTTNG_ERR_FATAL; goto close_error; } ret = lttcomm_recv_unix_sock(sock, (void *)&reply, sizeof(reply)); if (ret < 0) { ret = -LTTNG_ERR_FATAL; goto close_error; } ret = reply.ret_code; close_error: close(sock); error: return ret; }
static int run_as_cmd(struct run_as_worker *worker, enum run_as_cmd cmd, struct run_as_data *data, struct run_as_ret *ret_value, uid_t uid, gid_t gid) { int ret = 0; ssize_t readlen, writelen; /* * If we are non-root, we can only deal with our own uid. */ if (geteuid() != 0) { if (uid != geteuid()) { ret = -1; ret_value->_errno = EPERM; ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)", (int) uid, (int) geteuid()); goto end; } } data->cmd = cmd; data->uid = uid; data->gid = gid; /* * Stage 1: Send the run_as_data struct to the worker process */ writelen = lttcomm_send_unix_sock(worker->sockpair[0], data, sizeof(*data)); if (writelen < sizeof(*data)) { PERROR("Error writing message to run_as"); ret = -1; ret_value->_errno = EIO; goto end; } /* * Stage 2: Send file descriptor to the worker process if needed */ ret = send_fd_to_worker(worker, data->cmd, data->fd); if (ret) { PERROR("do_send_fd error"); ret = -1; ret_value->_errno = EIO; goto end; } /* * Stage 3: Wait for the execution of the command */ /* * Stage 4: Receive the run_as_ret struct containing the return value and * errno */ readlen = lttcomm_recv_unix_sock(worker->sockpair[0], ret_value, sizeof(*ret_value)); if (!readlen) { ERR("Run-as worker has hung-up during run_as_cmd"); ret = -1; ret_value->_errno = EIO; goto end; } else if (readlen < sizeof(*ret_value)) { 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; } /* * Stage 5: Receive file descriptor if needed */ ret = recv_fd_from_worker(worker, data->cmd, &ret_value->fd); if (ret < 0) { ERR("Error receiving fd"); ret = -1; ret_value->_errno = EIO; } end: return ret; }
/* * Return < 0 on error, 0 if OK, 1 on hangup. */ static int handle_one_cmd(struct run_as_worker *worker) { int ret = 0; struct run_as_data data; ssize_t readlen, writelen; struct run_as_ret sendret; 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 * its execution */ readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data, sizeof(data)); if (readlen == 0) { /* hang up */ ret = 1; goto end; } if (readlen < sizeof(data)) { PERROR("lttcomm_recv_unix_sock error"); ret = -1; goto end; } cmd = run_as_enum_to_fct(data.cmd); if (!cmd) { ret = -1; goto end; } /* * Stage 2: Receive file descriptor from master. * Some commands need a file descriptor as input so if it's needed we * receive the fd using the Unix socket. */ ret = recv_fd_from_master(worker, data.cmd, &data.fd); if (ret < 0) { PERROR("recv_fd_from_master error"); ret = -1; goto end; } prev_euid = getuid(); 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; } } /* * Also set umask to 0 for mkdir executable bit. */ umask(0); /* * Stage 3: Execute the command */ ret = (*cmd)(&data, &sendret); if (ret < 0) { DBG("Execution of command returned an error"); } write_return: ret = cleanup_received_fd(data.cmd, data.fd); if (ret < 0) { ERR("Error cleaning up FD"); goto end; } /* * Stage 4: Send run_as_ret structure to the master. * This structure contain the return value of the command and the errno. */ writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret, sizeof(sendret)); if (writelen < sizeof(sendret)) { PERROR("lttcomm_send_unix_sock error"); ret = -1; goto end; } /* * Stage 5: Send file descriptor to the master * Some commands return a file descriptor so if it's needed we pass it back * to the master using the Unix socket. */ ret = send_fd_to_master(worker, data.cmd, sendret.fd); if (ret < 0) { DBG("Sending FD to master returned an error"); goto end; } if (seteuid(prev_euid) < 0) { PERROR("seteuid"); ret = -1; goto end; } ret = 0; end: return ret; }
/* * Return < 0 on error, 0 if OK, 1 on hangup. */ static int handle_one_cmd(struct run_as_worker *worker) { int ret = 0; struct run_as_data data; ssize_t readlen, writelen; struct run_as_ret sendret; run_as_fct cmd; uid_t prev_euid; /* Read data */ readlen = lttcomm_recv_unix_sock(worker->sockpair[1], &data, sizeof(data)); if (readlen == 0) { /* hang up */ ret = 1; goto end; } if (readlen < sizeof(data)) { PERROR("lttcomm_recv_unix_sock error"); ret = -1; goto end; } cmd = run_as_enum_to_fct(data.cmd); if (!cmd) { ret = -1; goto end; } prev_euid = getuid(); if (data.gid != getegid()) { ret = setegid(data.gid); if (ret < 0) { PERROR("setegid"); goto write_return; } } if (data.uid != prev_euid) { ret = seteuid(data.uid); if (ret < 0) { PERROR("seteuid"); goto write_return; } } /* * Also set umask to 0 for mkdir executable bit. */ umask(0); ret = (*cmd)(&data); write_return: sendret.ret = ret; sendret._errno = errno; /* send back return value */ writelen = lttcomm_send_unix_sock(worker->sockpair[1], &sendret, sizeof(sendret)); if (writelen < sizeof(sendret)) { PERROR("lttcomm_send_unix_sock error"); ret = -1; goto end; } ret = do_send_fd(worker, data.cmd, ret); if (ret) { PERROR("do_send_fd error"); ret = -1; goto end; } if (seteuid(prev_euid) < 0) { PERROR("seteuid"); ret = -1; goto end; } ret = 0; end: return ret; }
/* * Thread managing health check socket. */ static void *thread_manage_health(void *data) { const bool is_root = (getuid() == 0); int sock = -1, new_sock = -1, ret, i, pollfd, err = -1; uint32_t revents, nb_fd; struct lttng_poll_event events; struct health_comm_msg msg; struct health_comm_reply reply; /* Thread-specific quit pipe. */ struct thread_notifiers *notifiers = data; const int quit_pipe_read_fd = lttng_pipe_get_readfd( notifiers->quit_pipe); DBG("[thread] Manage health check started"); rcu_register_thread(); /* * Created with a size of two for: * - client socket * - thread quit pipe */ ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC); if (ret < 0) { goto error; } /* Create unix socket */ sock = lttcomm_create_unix_sock(config.health_unix_sock_path.value); if (sock < 0) { ERR("Unable to create health check Unix socket"); goto error; } if (is_root) { /* lttng health client socket path permissions */ gid_t gid; ret = utils_get_group_id(config.tracing_group_name.value, true, &gid); if (ret) { /* Default to root group. */ gid = 0; } ret = chown(config.health_unix_sock_path.value, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", config.health_unix_sock_path.value); PERROR("chown"); goto error; } ret = chmod(config.health_unix_sock_path.value, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (ret < 0) { ERR("Unable to set permissions on %s", config.health_unix_sock_path.value); PERROR("chmod"); goto error; } } /* * Set the CLOEXEC flag. Return code is useless because either way, the * show must go on. */ (void) utils_set_fd_cloexec(sock); ret = lttcomm_listen_unix_sock(sock); if (ret < 0) { goto error; } ret = lttng_poll_add(&events, quit_pipe_read_fd, LPOLLIN | LPOLLERR); if (ret < 0) { goto error; } /* Add the application registration socket */ ret = lttng_poll_add(&events, sock, LPOLLIN | LPOLLPRI); if (ret < 0) { goto error; } mark_thread_as_ready(notifiers); while (1) { DBG("Health check ready"); /* Infinite blocking call, waiting for transmission */ restart: ret = lttng_poll_wait(&events, -1); if (ret < 0) { /* * Restart interrupted system call. */ if (errno == EINTR) { goto restart; } goto error; } nb_fd = ret; for (i = 0; i < nb_fd; i++) { /* Fetch once the poll data */ revents = LTTNG_POLL_GETEV(&events, i); pollfd = LTTNG_POLL_GETFD(&events, i); /* Event on the registration socket */ if (pollfd == sock) { if (revents & LPOLLIN) { continue; } else if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { ERR("Health socket poll error"); goto error; } else { ERR("Unexpected poll events %u for sock %d", revents, pollfd); goto error; } } else { /* Event on the thread's quit pipe. */ err = 0; goto exit; } } new_sock = lttcomm_accept_unix_sock(sock); if (new_sock < 0) { goto error; } /* * Set the CLOEXEC flag. Return code is useless because either way, the * show must go on. */ (void) utils_set_fd_cloexec(new_sock); DBG("Receiving data from client for health..."); ret = lttcomm_recv_unix_sock(new_sock, (void *)&msg, sizeof(msg)); if (ret <= 0) { DBG("Nothing recv() from client... continuing"); ret = close(new_sock); if (ret) { PERROR("close"); } continue; } rcu_thread_online(); memset(&reply, 0, sizeof(reply)); for (i = 0; i < NR_HEALTH_SESSIOND_TYPES; i++) { /* * health_check_state returns 0 if health is * bad. */ if (!health_check_state(health_sessiond, i)) { reply.ret_code |= 1ULL << i; } } DBG2("Health check return value %" PRIx64, reply.ret_code); ret = lttcomm_send_unix_sock(new_sock, (void *) &reply, sizeof(reply)); if (ret < 0) { ERR("Failed to send health data back to client"); } /* End of transmission */ ret = close(new_sock); if (ret) { PERROR("close"); } } exit: error: if (err) { ERR("Health error occurred in %s", __func__); } DBG("Health check thread dying"); unlink(config.health_unix_sock_path.value); if (sock >= 0) { ret = close(sock); if (ret) { PERROR("close"); } } lttng_poll_clean(&events); rcu_unregister_thread(); return NULL; }
static int handshake(struct lttng_notification_channel *channel) { ssize_t ret; enum lttng_notification_channel_status status = LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; struct lttng_notification_channel_command_handshake handshake = { .major = LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR, .minor = LTTNG_NOTIFICATION_CHANNEL_VERSION_MINOR, }; struct lttng_notification_channel_message msg_header = { .type = LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_HANDSHAKE, .size = sizeof(handshake), }; char send_buffer[sizeof(msg_header) + sizeof(handshake)]; memcpy(send_buffer, &msg_header, sizeof(msg_header)); memcpy(send_buffer + sizeof(msg_header), &handshake, sizeof(handshake)); pthread_mutex_lock(&channel->lock); ret = lttcomm_send_creds_unix_sock(channel->socket, send_buffer, sizeof(send_buffer)); if (ret < 0) { goto end_unlock; } /* Receive handshake info from the sessiond. */ ret = receive_command_reply(channel, &status); if (ret < 0) { goto end_unlock; } if (!channel->version.set) { ret = -1; goto end_unlock; } if (channel->version.major != LTTNG_NOTIFICATION_CHANNEL_VERSION_MAJOR) { ret = -1; goto end_unlock; } end_unlock: pthread_mutex_unlock(&channel->lock); return ret; } static enum lttng_notification_channel_status send_condition_command( struct lttng_notification_channel *channel, enum lttng_notification_channel_message_type type, const struct lttng_condition *condition) { int socket; ssize_t command_size, ret; enum lttng_notification_channel_status status = LTTNG_NOTIFICATION_CHANNEL_STATUS_OK; char *command_buffer = NULL; struct lttng_notification_channel_message cmd_message = { .type = type, }; if (!channel) { status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; goto end; } assert(type == LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_SUBSCRIBE || type == LTTNG_NOTIFICATION_CHANNEL_MESSAGE_TYPE_UNSUBSCRIBE); pthread_mutex_lock(&channel->lock); socket = channel->socket; if (!lttng_condition_validate(condition)) { status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; goto end_unlock; } ret = lttng_condition_serialize(condition, NULL); if (ret < 0) { status = LTTNG_NOTIFICATION_CHANNEL_STATUS_INVALID; goto end_unlock; } assert(ret < UINT32_MAX); cmd_message.size = (uint32_t) ret; command_size = ret + sizeof( struct lttng_notification_channel_message); command_buffer = zmalloc(command_size); if (!command_buffer) { goto end_unlock; } memcpy(command_buffer, &cmd_message, sizeof(cmd_message)); ret = lttng_condition_serialize(condition, command_buffer + sizeof(cmd_message)); if (ret < 0) { goto end_unlock; } ret = lttcomm_send_unix_sock(socket, command_buffer, command_size); if (ret < 0) { status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; goto end_unlock; } ret = receive_command_reply(channel, &status); if (ret < 0) { status = LTTNG_NOTIFICATION_CHANNEL_STATUS_ERROR; goto end_unlock; } end_unlock: pthread_mutex_unlock(&channel->lock); end: free(command_buffer); return status; }