X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng-relayd%2Fmain.c;h=16bfccbfa0c982cd3c7fcd33c0ed1ea0999369cf;hp=b3983b163fb32a5b13c593573f92fbfab75b5ca0;hb=9c256b0146b021962e93fda7400c61ceb2b0d45f;hpb=c70636a7342f34e3be68fcf411cf3e3718b8e73f diff --git a/src/bin/lttng-relayd/main.c b/src/bin/lttng-relayd/main.c index b3983b163..16bfccbfa 100644 --- a/src/bin/lttng-relayd/main.c +++ b/src/bin/lttng-relayd/main.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include @@ -61,6 +63,8 @@ #include #include #include +#include +#include #include "backward-compatibility-group-by.h" #include "cmd.h" @@ -98,7 +102,7 @@ enum relay_connection_status { /* command line options */ char *opt_output_path, *opt_working_directory; -static int opt_daemon, opt_background, opt_print_version; +static int opt_daemon, opt_background, opt_print_version, opt_allow_clear = 1; enum relay_group_output_by opt_group_output_by = RELAYD_GROUP_OUTPUT_BY_UNKNOWN; /* @@ -160,6 +164,9 @@ static uint64_t last_relay_stream_id; */ static struct relay_conn_queue relay_conn_queue; +/* Cap of file desriptors to be in simultaneous use by the relay daemon. */ +static unsigned int lttng_opt_fd_cap; + /* Global relay stream hash table. */ struct lttng_ht *relay_streams_ht; @@ -174,6 +181,9 @@ struct health_app *health_relayd; struct sessiond_trace_chunk_registry *sessiond_trace_chunk_registry; +/* Global fd tracker. */ +struct fd_tracker *the_fd_tracker; + static struct option long_options[] = { { "control-port", 1, 0, 'C', }, { "data-port", 1, 0, 'D', }, @@ -181,6 +191,7 @@ static struct option long_options[] = { { "daemonize", 0, 0, 'd', }, { "background", 0, 0, 'b', }, { "group", 1, 0, 'g', }, + { "fd-cap", 1, 0, '\0', }, { "help", 0, 0, 'h', }, { "output", 1, 0, 'o', }, { "verbose", 0, 0, 'v', }, @@ -189,6 +200,7 @@ static struct option long_options[] = { { "working-directory", 1, 0, 'w', }, { "group-output-by-session", 0, 0, 's', }, { "group-output-by-host", 0, 0, 'p', }, + { "disallow-clear", 0, 0, 'x' }, { NULL, 0, 0, 0, }, }; @@ -223,9 +235,34 @@ static int set_option(int opt, const char *arg, const char *optname) switch (opt) { case 0: - fprintf(stderr, "option %s", optname); - if (arg) { - fprintf(stderr, " with arg %s\n", arg); + if (!strcmp(optname, "fd-cap")) { + unsigned long v; + + errno = 0; + v = strtoul(arg, NULL, 0); + if (errno != 0 || !isdigit(arg[0])) { + ERR("Wrong value in --fd-cap parameter: %s", + arg); + ret = -1; + goto end; + } + if (v < DEFAULT_RELAYD_MINIMAL_FD_CAP) { + ERR("File descriptor cap must be set to at least %d", + DEFAULT_RELAYD_MINIMAL_FD_CAP); + } + if (v >= UINT_MAX) { + ERR("File descriptor cap overflow in --fd-cap parameter: %s", + arg); + ret = -1; + goto end; + } + lttng_opt_fd_cap = (unsigned int) v; + DBG3("File descriptor cap set to %u", lttng_opt_fd_cap); + } else { + fprintf(stderr, "unknown option %s", optname); + if (arg) { + fprintf(stderr, " with arg %s\n", arg); + } } break; case 'C': @@ -355,6 +392,10 @@ static int set_option(int opt, const char *arg, const char *optname) } opt_group_output_by = RELAYD_GROUP_OUTPUT_BY_HOST; break; + case 'x': + /* Disallow clear */ + opt_allow_clear = 0; + break; default: /* Unknown option or other error. * Error is printed by getopt, just return */ @@ -558,10 +599,35 @@ static int set_options(int argc, char **argv) goto exit; } } + if (lttng_opt_fd_cap == 0) { + int ret; + struct rlimit rlimit; + + ret = getrlimit(RLIMIT_NOFILE, &rlimit); + if (ret) { + PERROR("Failed to get file descriptor limit"); + retval = -1; + } + + lttng_opt_fd_cap = rlimit.rlim_cur; + } if (opt_group_output_by == RELAYD_GROUP_OUTPUT_BY_UNKNOWN) { opt_group_output_by = RELAYD_GROUP_OUTPUT_BY_HOST; } + if (opt_allow_clear) { + /* Check if env variable exists. */ + const char *value = lttng_secure_getenv(DEFAULT_LTTNG_RELAYD_DISALLOW_CLEAR_ENV); + if (value) { + ret = config_parse_value(value); + if (ret < 0) { + ERR("Invalid value for %s specified", DEFAULT_LTTNG_RELAYD_DISALLOW_CLEAR_ENV); + retval = -1; + goto exit; + } + opt_allow_clear = !ret; + } + } exit: free(optstring); @@ -570,13 +636,9 @@ exit: static void print_global_objects(void) { - rcu_register_thread(); - print_viewer_streams(); print_relay_streams(); print_sessions(); - - rcu_unregister_thread(); } /* @@ -598,8 +660,30 @@ static void relayd_cleanup(void) free(opt_output_path); free(opt_working_directory); + if (health_relayd) { + health_app_destroy(health_relayd); + } /* Close thread quit pipes */ - utils_close_pipe(thread_quit_pipe); + if (health_quit_pipe[0] != -1) { + (void) fd_tracker_util_pipe_close( + the_fd_tracker, health_quit_pipe); + } + if (thread_quit_pipe[0] != -1) { + (void) fd_tracker_util_pipe_close( + the_fd_tracker, thread_quit_pipe); + } + if (sessiond_trace_chunk_registry) { + sessiond_trace_chunk_registry_destroy( + sessiond_trace_chunk_registry); + } + if (the_fd_tracker) { + untrack_stdio(); + /* + * fd_tracker_destroy() will log the contents of the fd-tracker + * if a leak is detected. + */ + fd_tracker_destroy(the_fd_tracker); + } uri_free(control_uri); uri_free(data_uri); @@ -761,17 +845,26 @@ void lttng_relay_notify_ready(void) */ static int init_thread_quit_pipe(void) { - int ret; - - ret = utils_create_pipe_cloexec(thread_quit_pipe); + return fd_tracker_util_pipe_open_cloexec( + the_fd_tracker, "Quit pipe", thread_quit_pipe); +} - return ret; +/* + * Init health quit pipe. + * + * Return -1 on error or 0 if all pipes are created. + */ +static int init_health_quit_pipe(void) +{ + return fd_tracker_util_pipe_open_cloexec(the_fd_tracker, + "Health quit pipe", health_quit_pipe); } /* * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. */ -static int create_thread_poll_set(struct lttng_poll_event *events, int size) +static int create_named_thread_poll_set(struct lttng_poll_event *events, + int size, const char *name) { int ret; @@ -780,10 +873,8 @@ static int create_thread_poll_set(struct lttng_poll_event *events, int size) goto error; } - ret = lttng_poll_create(events, size, LTTNG_CLOEXEC); - if (ret < 0) { - goto error; - } + ret = fd_tracker_util_poll_create(the_fd_tracker, + name, events, 1, LTTNG_CLOEXEC); /* Add quit pipe */ ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN | LPOLLERR); @@ -882,7 +973,7 @@ static void *relay_thread_listener(void *data) * Pass 3 as size here for the thread quit pipe, control and * data socket. */ - ret = create_thread_poll_set(&events, 3); + ret = create_named_thread_poll_set(&events, 3, "Listener thread epoll"); if (ret < 0) { goto error_create_poll; } @@ -1016,7 +1107,7 @@ exit: error: error_poll_add: error_testpoint: - lttng_poll_clean(&events); + (void) fd_tracker_util_poll_clean(the_fd_tracker, &events); error_create_poll: if (data_sock->fd >= 0) { ret = data_sock->ops->close(data_sock); @@ -1521,7 +1612,7 @@ static int relay_close_stream(const struct lttcomm_relayd_hdr *recv_hdr, vstream = viewer_stream_get_by_id(stream->stream_handle); if (vstream) { - if (vstream->metadata_sent == stream->metadata_received) { + if (stream->no_new_metadata_notified) { /* * Since all the metadata has been sent to the * viewer and that we have a request to close @@ -2188,6 +2279,9 @@ static int relay_recv_index(const struct lttcomm_relayd_hdr *recv_hdr, index_info.stream_instance_id = be64toh(index_info.stream_instance_id); index_info.packet_seq_num = be64toh(index_info.packet_seq_num); + } else { + index_info.stream_instance_id = -1ULL; + index_info.packet_seq_num = -1ULL; } stream = stream_get_by_id(index_info.relay_stream_id); @@ -2351,7 +2445,6 @@ static int relay_rotate_session_streams( } else { chunk_id_str = chunk_id_buf; } - session->has_rotated = true; } DBG("Rotate %" PRIu32 " streams of session \"%s\" to chunk \"%s\"", @@ -2437,7 +2530,8 @@ static int relay_create_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, struct lttng_trace_chunk *chunk = NULL, *published_chunk = NULL; enum lttng_error_code reply_code = LTTNG_OK; enum lttng_trace_chunk_status chunk_status; - struct lttng_directory_handle session_output; + struct lttng_directory_handle *session_output = NULL; + const char *new_path; if (!session || !conn->version_check_done) { ERR("Trying to create a trace chunk before version check"); @@ -2464,8 +2558,29 @@ static int relay_create_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, msg->creation_timestamp = be64toh(msg->creation_timestamp); msg->override_name_length = be32toh(msg->override_name_length); + if (session->current_trace_chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + DEFAULT_CHUNK_TMP_OLD_DIRECTORY); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ERR("Failed to rename old chunk"); + ret = -1; + reply_code = LTTNG_ERR_UNK; + goto end; + } + } + session->ongoing_rotation = true; + if (!session->current_trace_chunk) { + if (!session->has_rotated) { + new_path = ""; + } else { + new_path = NULL; + } + } else { + new_path = DEFAULT_CHUNK_TMP_NEW_DIRECTORY; + } chunk = lttng_trace_chunk_create( - msg->chunk_id, msg->creation_timestamp); + msg->chunk_id, msg->creation_timestamp, new_path); if (!chunk) { ERR("Failed to create trace chunk in trace chunk creation command"); ret = -1; @@ -2512,14 +2627,15 @@ static int relay_create_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, goto end; } - ret = session_init_output_directory_handle( - conn->session, &session_output); - if (ret) { + session_output = session_create_output_directory_handle( + conn->session); + if (!session_output) { reply_code = LTTNG_ERR_CREATE_DIR_FAIL; goto end; } - chunk_status = lttng_trace_chunk_set_as_owner(chunk, &session_output); - lttng_directory_handle_fini(&session_output); + chunk_status = lttng_trace_chunk_set_as_owner(chunk, session_output); + lttng_directory_handle_put(session_output); + session_output = NULL; if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { reply_code = LTTNG_ERR_UNK; ret = -1; @@ -2559,6 +2675,9 @@ static int relay_create_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, conn->session->current_trace_chunk; conn->session->current_trace_chunk = published_chunk; published_chunk = NULL; + if (!conn->session->pending_closure_trace_chunk) { + session->ongoing_rotation = false; + } end_unlock_session: pthread_mutex_unlock(&conn->session->lock); end: @@ -2575,6 +2694,7 @@ end: end_no_reply: lttng_trace_chunk_put(chunk); lttng_trace_chunk_put(published_chunk); + lttng_directory_handle_put(session_output); return ret; } @@ -2601,6 +2721,7 @@ static int relay_close_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, size_t path_length = 0; const char *chunk_name = NULL; struct lttng_dynamic_buffer reply_payload; + const char *new_path; lttng_dynamic_buffer_init(&reply_payload); @@ -2651,6 +2772,20 @@ static int relay_close_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, } pthread_mutex_lock(&session->lock); + if (close_command.is_set && + close_command.value == LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE) { + /* + * Clear command. It is a protocol error to ask for a + * clear on a relay which does not allow it. Querying + * the configuration allows figuring out whether + * clearing is allowed before doing the clear. + */ + if (!opt_allow_clear) { + ret = -1; + reply_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end_unlock_session; + } + } if (session->pending_closure_trace_chunk && session->pending_closure_trace_chunk != chunk) { ERR("Trace chunk close command for session \"%s\" does not target the trace chunk pending closure", @@ -2660,6 +2795,43 @@ static int relay_close_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, goto end_unlock_session; } + if (session->current_trace_chunk && session->current_trace_chunk != chunk && + !lttng_trace_chunk_get_name_overridden(session->current_trace_chunk)) { + if (close_command.is_set && + close_command.value == LTTNG_TRACE_CHUNK_COMMAND_TYPE_DELETE && + !session->has_rotated) { + /* New chunk stays in session output directory. */ + new_path = ""; + } else { + /* Use chunk name for new chunk. */ + new_path = NULL; + } + /* Rename new chunk path. */ + chunk_status = lttng_trace_chunk_rename_path(session->current_trace_chunk, + new_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + session->ongoing_rotation = false; + } + if ((!close_command.is_set || + close_command.value == LTTNG_TRACE_CHUNK_COMMAND_TYPE_NO_OPERATION) && + !lttng_trace_chunk_get_name_overridden(chunk)) { + const char *old_path; + + if (!session->has_rotated) { + old_path = ""; + } else { + old_path = NULL; + } + /* We need to move back the .tmp_old_chunk to its rightful place. */ + chunk_status = lttng_trace_chunk_rename_path(chunk, old_path); + if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { + ret = -1; + goto end; + } + } chunk_status = lttng_trace_chunk_set_close_timestamp( chunk, close_timestamp); if (chunk_status != LTTNG_TRACE_CHUNK_STATUS_OK) { @@ -2717,6 +2889,10 @@ static int relay_close_trace_chunk(const struct lttcomm_relayd_hdr *recv_hdr, goto end_unlock_session; } } + if (close_command.is_set && + close_command.value == LTTNG_TRACE_CHUNK_COMMAND_TYPE_MOVE_TO_COMPLETED) { + session->has_rotated = true; + } DBG("Reply chunk path on close: %s", closed_trace_chunk_path); path_length = strlen(closed_trace_chunk_path) + 1; if (path_length > UINT32_MAX) { @@ -2843,6 +3019,57 @@ end_no_reply: return ret; } +/* + * relay_get_configuration: query whether feature is available + */ +static int relay_get_configuration(const struct lttcomm_relayd_hdr *recv_hdr, + struct relay_connection *conn, + const struct lttng_buffer_view *payload) +{ + int ret = 0; + ssize_t send_ret; + struct lttcomm_relayd_get_configuration *msg; + struct lttcomm_relayd_get_configuration_reply reply = {}; + struct lttng_buffer_view header_view; + uint64_t query_flags = 0; + uint64_t result_flags = 0; + + header_view = lttng_buffer_view_from_view(payload, 0, sizeof(*msg)); + if (!header_view.data) { + ERR("Failed to receive payload of chunk close command"); + ret = -1; + goto end_no_reply; + } + + /* Convert to host endianness. */ + msg = (typeof(msg)) header_view.data; + query_flags = be64toh(msg->query_flags); + + if (query_flags) { + ret = LTTNG_ERR_INVALID_PROTOCOL; + goto reply; + } + if (opt_allow_clear) { + result_flags |= LTTCOMM_RELAYD_CONFIGURATION_FLAG_CLEAR_ALLOWED; + } + ret = 0; +reply: + reply = (typeof(reply)){ + .generic.ret_code = htobe32((uint32_t) + (ret == 0 ? LTTNG_OK : LTTNG_ERR_INVALID_PROTOCOL)), + .relayd_configuration_flags = htobe64(result_flags), + }; + send_ret = conn->sock->ops->sendmsg( + conn->sock, &reply, sizeof(reply), 0); + if (send_ret < (ssize_t) sizeof(reply)) { + ERR("Failed to send \"get configuration\" command reply (ret = %zd)", + send_ret); + ret = -1; + } +end_no_reply: + return ret; +} + #define DBG_CMD(cmd_name, conn) \ DBG3("Processing \"%s\" command for socket %i", cmd_name, conn->sock->fd); @@ -2921,6 +3148,10 @@ static int relay_process_control_command(struct relay_connection *conn, DBG_CMD("RELAYD_TRACE_CHUNK_EXISTS", conn); ret = relay_trace_chunk_exists(header, conn, payload); break; + case RELAYD_GET_CONFIGURATION: + DBG_CMD("RELAYD_GET_CONFIGURATION", conn); + ret = relay_get_configuration(header, conn, payload); + break; case RELAYD_UPDATE_SYNC_INFO: default: ERR("Received unknown command (%u)", header->cmd); @@ -3459,7 +3690,7 @@ static void *relay_thread_worker(void *data) goto relay_connections_ht_error; } - ret = create_thread_poll_set(&events, 2); + ret = create_named_thread_poll_set(&events, 2, "Worker thread epoll"); if (ret < 0) { goto error_poll_create; } @@ -3728,12 +3959,13 @@ error: } rcu_read_unlock(); - lttng_poll_clean(&events); + (void) fd_tracker_util_poll_clean(the_fd_tracker, &events); error_poll_create: lttng_ht_destroy(relay_connections_ht); relay_connections_ht_error: /* Close relay conn pipes */ - utils_close_pipe(relay_conn_pipe); + (void) fd_tracker_util_pipe_close(the_fd_tracker, + relay_conn_pipe); if (err) { DBG("Thread exited with error"); } @@ -3755,11 +3987,41 @@ error_testpoint: */ static int create_relay_conn_pipe(void) { - int ret; + return fd_tracker_util_pipe_open_cloexec(the_fd_tracker, + "Relayd connection pipe", relay_conn_pipe); +} - ret = utils_create_pipe_cloexec(relay_conn_pipe); +static int stdio_open(void *data, int *fds) +{ + fds[0] = fileno(stdout); + fds[1] = fileno(stderr); + return 0; +} - return ret; +static int noop_close(void *data, int *fds) +{ + return 0; +} + +static int track_stdio(void) +{ + int fds[2]; + const char *names[] = { "stdout", "stderr" }; + + return fd_tracker_open_unsuspendable_fd(the_fd_tracker, fds, + names, 2, stdio_open, NULL); +} + +static void untrack_stdio(void) +{ + int fds[] = { fileno(stdout), fileno(stderr) }; + + /* + * noop_close is used since we don't really want to close + * the stdio output fds; we merely want to stop tracking them. + */ + (void) fd_tracker_close_unsuspendable_fd(the_fd_tracker, + fds, 2, noop_close, NULL); } /* @@ -3767,6 +4029,7 @@ static int create_relay_conn_pipe(void) */ int main(int argc, char **argv) { + bool thread_is_rcu_registered = false; int ret = 0, retval = 0; void *status; @@ -3806,6 +4069,8 @@ int main(int argc, char **argv) goto exit_options; } + DBG("Clear command %s", opt_allow_clear ? "allowed" : "disallowed"); + /* Try to create directory if -o, --output is specified. */ if (opt_output_path) { if (*opt_output_path != '/') { @@ -3825,23 +4090,12 @@ int main(int argc, char **argv) /* Daemonize */ if (opt_daemon || opt_background) { - int i; - ret = lttng_daemonize(&child_ppid, &recv_child_signal, !opt_background); if (ret < 0) { retval = -1; goto exit_options; } - - /* - * We are in the child. Make sure all other file - * descriptors are closed, in case we are called with - * more opened file descriptors than the standard ones. - */ - for (i = 3; i < sysconf(_SC_OPEN_MAX); i++) { - (void) close(i); - } } if (opt_working_directory) { @@ -3856,7 +4110,28 @@ int main(int argc, char **argv) if (!sessiond_trace_chunk_registry) { ERR("Failed to initialize session daemon trace chunk registry"); retval = -1; - goto exit_sessiond_trace_chunk_registry; + goto exit_options; + } + + /* + * The RCU thread registration (and use, through the fd-tracker's + * creation) is done after the daemonization to allow us to not + * deal with liburcu's fork() management as the call RCU needs to + * be restored. + */ + rcu_register_thread(); + thread_is_rcu_registered = true; + + the_fd_tracker = fd_tracker_create(lttng_opt_fd_cap); + if (!the_fd_tracker) { + retval = -1; + goto exit_options; + } + + ret = track_stdio(); + if (ret) { + retval = -1; + goto exit_options; } /* Initialize thread health monitoring */ @@ -3864,19 +4139,19 @@ int main(int argc, char **argv) if (!health_relayd) { PERROR("health_app_create error"); retval = -1; - goto exit_health_app_create; + goto exit_options; } /* Create thread quit pipe */ if (init_thread_quit_pipe()) { retval = -1; - goto exit_init_data; + goto exit_options; } /* Setup the thread apps communication pipe. */ if (create_relay_conn_pipe()) { retval = -1; - goto exit_init_data; + goto exit_options; } /* Init relay command queue. */ @@ -3890,27 +4165,27 @@ int main(int argc, char **argv) sessions_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); if (!sessions_ht) { retval = -1; - goto exit_init_data; + goto exit_options; } /* tables of streams indexed by stream ID */ relay_streams_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); if (!relay_streams_ht) { retval = -1; - goto exit_init_data; + goto exit_options; } /* tables of streams indexed by stream ID */ viewer_streams_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64); if (!viewer_streams_ht) { retval = -1; - goto exit_init_data; + goto exit_options; } - ret = utils_create_pipe(health_quit_pipe); + ret = init_health_quit_pipe(); if (ret) { retval = -1; - goto exit_health_quit_pipe; + goto exit_options; } /* Create thread to manage the client socket */ @@ -3920,7 +4195,7 @@ int main(int argc, char **argv) errno = ret; PERROR("pthread_create health"); retval = -1; - goto exit_health_thread; + goto exit_options; } /* Setup the dispatcher thread */ @@ -4001,16 +4276,6 @@ exit_dispatcher_thread: PERROR("pthread_join health_thread"); retval = -1; } -exit_health_thread: - - utils_close_pipe(health_quit_pipe); -exit_health_quit_pipe: - -exit_init_data: - health_app_destroy(health_relayd); - sessiond_trace_chunk_registry_destroy(sessiond_trace_chunk_registry); -exit_health_app_create: -exit_sessiond_trace_chunk_registry: exit_options: /* * Wait for all pending call_rcu work to complete before tearing @@ -4023,6 +4288,10 @@ exit_options: /* Ensure all prior call_rcu are done. */ rcu_barrier(); + if (thread_is_rcu_registered) { + rcu_unregister_thread(); + } + if (!retval) { exit(EXIT_SUCCESS); } else {