X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=ltt-sessiond%2Fmain.c;h=5db41869f5b1e8c5176cbe07ca42b79e302e52a8;hp=5636700d8fad1cd3127c9c6571b03a648782261d;hb=e8b608574c1ece1984f094388f8da2a0b4b3b83b;hpb=d0254c7c0bb43c62abafdf866b16de0da1ad4d5d diff --git a/ltt-sessiond/main.c b/ltt-sessiond/main.c index 5636700d8..5db41869f 100644 --- a/ltt-sessiond/main.c +++ b/ltt-sessiond/main.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 - David Goulet + * Copyright (C) 2011 - Mathieu Desnoyers * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,9 +41,9 @@ #include /* URCU list library (-lurcu) */ #include +#include #include "context.h" -#include "liblttsessiondcomm.h" #include "ltt-sessiond.h" #include "lttngerr.h" #include "kernel-ctl.h" @@ -60,6 +61,7 @@ const char default_global_apps_pipe[] = DEFAULT_GLOBAL_APPS_PIPE; /* Variables */ int opt_verbose; /* Not static for lttngerr.h */ +int opt_verbose_kconsumerd; /* Not static for lttngerr.h */ int opt_quiet; /* Not static for lttngerr.h */ const char *progname; @@ -113,6 +115,22 @@ static int modprobe_remove_kernel_modules(void); */ static struct ltt_session_list *session_list_ptr; +static gid_t allowed_group(void) +{ + struct group *grp; + + if (opt_tracing_group) { + grp = getgrnam(opt_tracing_group); + } else { + grp = getgrnam(default_tracing_group); + } + if (!grp) { + return -1; + } else { + return grp->gr_gid; + } +} + /* * Init quit pipe. * @@ -140,16 +158,33 @@ static void teardown_kernel_session(struct ltt_session *session) { if (session->kernel_session != NULL) { DBG("Tearing down kernel session"); + + /* + * If a custom kernel consumer was registered, close the socket before + * tearing down the complete kernel session structure + */ + if (session->kernel_session->consumer_fd != kconsumerd_cmd_sock) { + lttcomm_close_unix_sock(session->kernel_session->consumer_fd); + } + trace_destroy_kernel_session(session->kernel_session); /* Extra precaution */ session->kernel_session = NULL; } } +static void stop_threads(void) +{ + /* Stopping all threads */ + DBG("Terminating all threads"); + close(thread_quit_pipe[0]); + close(thread_quit_pipe[1]); +} + /* * Cleanup the daemon */ -static void cleanup() +static void cleanup(void) { int ret; char *cmd; @@ -163,11 +198,6 @@ static void cleanup() 27, 1, 31, 27, 0, 27, 1, 33, 27, 0); /* */ - /* Stopping all threads */ - DBG("Terminating all threads"); - close(thread_quit_pipe[0]); - close(thread_quit_pipe[1]); - DBG("Removing %s directory", LTTNG_RUNDIR); ret = asprintf(&cmd, "rm -rf " LTTNG_RUNDIR); if (ret < 0) { @@ -267,7 +297,9 @@ static int send_kconsumerd_channel_fds(int sock, struct ltt_kernel_channel *chan lkm.fd = stream->fd; lkm.state = stream->state; lkm.max_sb_size = channel->channel->attr.subbuf_size; + lkm.output = channel->channel->attr.output; strncpy(lkm.path_name, stream->pathname, PATH_MAX); + lkm.path_name[PATH_MAX - 1] = '\0'; DBG("Sending fd %d to kconsumerd", lkm.fd); @@ -290,7 +322,7 @@ error: /* * Send all stream fds of the kernel session to the consumer. */ -static int send_kconsumerd_fds(int sock, struct ltt_kernel_session *session) +static int send_kconsumerd_fds(struct ltt_kernel_session *session) { int ret; struct ltt_kernel_channel *chan; @@ -303,7 +335,7 @@ static int send_kconsumerd_fds(int sock, struct ltt_kernel_session *session) DBG("Sending kconsumerd header for metadata"); - ret = lttcomm_send_unix_sock(sock, &lkh, sizeof(struct lttcomm_kconsumerd_header)); + ret = lttcomm_send_unix_sock(session->consumer_fd, &lkh, sizeof(struct lttcomm_kconsumerd_header)); if (ret < 0) { perror("send kconsumerd header"); goto error; @@ -311,14 +343,21 @@ static int send_kconsumerd_fds(int sock, struct ltt_kernel_session *session) DBG("Sending metadata stream fd"); + /* Extra protection. It's NOT suppose to be set to 0 at this point */ + if (session->consumer_fd == 0) { + session->consumer_fd = kconsumerd_cmd_sock; + } + if (session->metadata_stream_fd != 0) { /* Send metadata stream fd first */ lkm.fd = session->metadata_stream_fd; lkm.state = ACTIVE_FD; lkm.max_sb_size = session->metadata->conf->attr.subbuf_size; + lkm.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; strncpy(lkm.path_name, session->metadata->pathname, PATH_MAX); + lkm.path_name[PATH_MAX - 1] = '\0'; - ret = lttcomm_send_fds_unix_sock(sock, &lkm, &lkm.fd, 1, sizeof(lkm)); + ret = lttcomm_send_fds_unix_sock(session->consumer_fd, &lkm, &lkm.fd, 1, sizeof(lkm)); if (ret < 0) { perror("send kconsumerd fd"); goto error; @@ -326,7 +365,7 @@ static int send_kconsumerd_fds(int sock, struct ltt_kernel_session *session) } cds_list_for_each_entry(chan, &session->channel_list.head, list) { - ret = send_kconsumerd_channel_fds(sock, chan); + ret = send_kconsumerd_channel_fds(session->consumer_fd, chan); if (ret < 0) { goto error; } @@ -525,6 +564,12 @@ static int update_kernel_stream(int fd) unlock_session(session); continue; } + + /* This is not suppose to be 0 but this is an extra security check */ + if (session->kernel_session->consumer_fd == 0) { + session->kernel_session->consumer_fd = kconsumerd_cmd_sock; + } + cds_list_for_each_entry(channel, &session->kernel_session->channel_list.head, list) { if (channel->fd == fd) { DBG("Channel found, updating kernel streams"); @@ -532,12 +577,14 @@ static int update_kernel_stream(int fd) if (ret < 0) { goto end; } + /* * Have we already sent fds to the consumer? If yes, it means that * tracing is started so it is safe to send our updated stream fds. */ if (session->kernel_session->kconsumer_fds_sent == 1) { - ret = send_kconsumerd_channel_fds(kconsumerd_cmd_sock, channel); + ret = send_kconsumerd_channel_fds(session->kernel_session->consumer_fd, + channel); if (ret < 0) { goto end; } @@ -862,6 +909,26 @@ error: return ret; } +/* + * Join kernel consumer thread + */ +static int join_kconsumerd_thread(void) +{ + void *status; + int ret; + + if (kconsumerd_pid != 0) { + ret = kill(kconsumerd_pid, SIGTERM); + if (ret) { + ERR("Error killing kconsumerd"); + return ret; + } + return pthread_join(kconsumerd_thread, &status); + } else { + return 0; + } +} + /* * Fork and exec a kernel consumer daemon (kconsumerd). * @@ -880,7 +947,7 @@ static pid_t spawn_kconsumerd(void) /* * Exec kconsumerd. */ - if (opt_verbose > 1) { + if (opt_verbose > 1 || opt_verbose_kconsumerd) { verbosity = "--verbose"; } else { verbosity = "--quiet"; @@ -967,7 +1034,7 @@ static int modprobe_kernel_modules(void) ERR("Unable to launch modprobe for module %s", kernel_modules_list[i].name); } else if (kernel_modules_list[i].required - && WEXITSTATUS(ret) != 0) { + && WEXITSTATUS(ret) != 0) { ERR("Unable to load module %s", kernel_modules_list[i].name); } else { @@ -1003,7 +1070,7 @@ static int modprobe_remove_kernel_modules(void) ERR("Unable to launch modprobe --remove for module %s", kernel_modules_list[i].name); } else if (kernel_modules_list[i].required - && WEXITSTATUS(ret) != 0) { + && WEXITSTATUS(ret) != 0) { ERR("Unable to remove module %s", kernel_modules_list[i].name); } else { @@ -1024,7 +1091,7 @@ static int mount_debugfs(char *path) int ret; char *type = "debugfs"; - ret = mkdir_recursive(path, S_IRWXU | S_IRWXG); + ret = mkdir_recursive(path, S_IRWXU | S_IRWXG, geteuid(), getegid()); if (ret < 0) { goto error; } @@ -1131,7 +1198,16 @@ static int start_kernel_trace(struct ltt_kernel_session *session) int ret = 0; if (session->kconsumer_fds_sent == 0) { - ret = send_kconsumerd_fds(kconsumerd_cmd_sock, session); + /* + * Assign default kernel consumer if no consumer assigned to the kernel + * session. At this point, it's NOT suppose to be 0 but this is an extra + * security check. + */ + if (session->consumer_fd == 0) { + session->consumer_fd = kconsumerd_cmd_sock; + } + + ret = send_kconsumerd_fds(session); if (ret < 0) { ERR("Send kconsumerd fds failed"); ret = LTTCOMM_KERN_CONSUMER_FAIL; @@ -1205,9 +1281,22 @@ static int create_kernel_session(struct ltt_session *session) goto error; } - ret = mkdir_recursive(session->path, S_IRWXU | S_IRWXG ); + /* Set kernel consumer socket fd */ + if (kconsumerd_cmd_sock) { + session->kernel_session->consumer_fd = kconsumerd_cmd_sock; + } + + ret = asprintf(&session->kernel_session->trace_path, "%s/kernel", + session->path); + if (ret < 0) { + perror("asprintf kernel traces path"); + goto error; + } + + ret = mkdir_recursive(session->kernel_session->trace_path, + S_IRWXU | S_IRWXG, geteuid(), allowed_group()); if (ret < 0) { - if (ret != EEXIST) { + if (ret != -EEXIST) { ERR("Trace directory creation error"); goto error; } @@ -1236,7 +1325,9 @@ static void list_lttng_sessions(struct lttng_session *sessions) */ cds_list_for_each_entry(session, &session_list_ptr->head, list) { strncpy(sessions[i].path, session->path, PATH_MAX); + sessions[i].path[PATH_MAX - 1] = '\0'; strncpy(sessions[i].name, session->name, NAME_MAX); + sessions[i].name[NAME_MAX - 1] = '\0'; i++; } } @@ -1282,6 +1373,7 @@ static void list_lttng_events(struct ltt_kernel_channel *kchan, /* Kernel channels */ cds_list_for_each_entry(event, &kchan->events_list.head , list) { strncpy(events[i].name, event->event->name, LTTNG_SYMBOL_NAME_LEN); + events[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; events[i].enabled = event->enabled; switch (event->event->instrumentation) { case LTTNG_KERNEL_TRACEPOINT: @@ -1316,7 +1408,9 @@ static int process_client_msg(struct command_ctx *cmd_ctx) DBG("Processing client command %d", cmd_ctx->lsm->cmd_type); - /* Listing commands don't need a session */ + /* + * Commands that DO NOT need a session. + */ switch (cmd_ctx->lsm->cmd_type) { case LTTNG_CREATE_SESSION: case LTTNG_LIST_SESSIONS: @@ -1357,10 +1451,10 @@ static int process_client_msg(struct command_ctx *cmd_ctx) /* Need a session for kernel command */ switch (cmd_ctx->lsm->cmd_type) { + case LTTNG_CALIBRATE: case LTTNG_CREATE_SESSION: case LTTNG_LIST_SESSIONS: case LTTNG_LIST_TRACEPOINTS: - case LTTNG_CALIBRATE: break; default: if (cmd_ctx->session->kernel_session == NULL) { @@ -1371,7 +1465,9 @@ static int process_client_msg(struct command_ctx *cmd_ctx) } /* Start the kernel consumer daemon */ - if (kconsumerd_pid == 0) { + + if (kconsumerd_pid == 0 && + cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { ret = start_kconsumerd(); if (ret < 0) { goto error; @@ -1405,6 +1501,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) strncpy(kctx.u.perf_counter.name, cmd_ctx->lsm->u.context.ctx.u.perf_counter.name, LTTNG_SYMBOL_NAME_LEN); + kctx.u.perf_counter.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; /* Add kernel context to kernel tracer. See context.c */ ret = add_kernel_context(cmd_ctx->session->kernel_session, &kctx, @@ -1564,7 +1661,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx) DBG("Creating kernel channel"); ret = kernel_create_channel(cmd_ctx->session->kernel_session, - &cmd_ctx->lsm->u.channel.chan, cmd_ctx->session->path); + &cmd_ctx->lsm->u.channel.chan, + cmd_ctx->session->kernel_session->trace_path); if (ret < 0) { ret = LTTCOMM_KERN_CHAN_FAIL; goto error; @@ -1627,7 +1725,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) } ret = kernel_create_channel(cmd_ctx->session->kernel_session, - chan, cmd_ctx->session->path); + chan, cmd_ctx->session->kernel_session->trace_path); if (ret < 0) { ret = LTTCOMM_KERN_CHAN_FAIL; goto error; @@ -1699,7 +1797,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) } ret = kernel_create_channel(cmd_ctx->session->kernel_session, - chan, cmd_ctx->session->path); + chan, cmd_ctx->session->kernel_session->trace_path); if (ret < 0) { ret = LTTCOMM_KERN_CHAN_FAIL; goto error; @@ -1804,7 +1902,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) if (cmd_ctx->session->kernel_session->metadata == NULL) { DBG("Open kernel metadata"); ret = kernel_open_metadata(cmd_ctx->session->kernel_session, - cmd_ctx->session->path); + cmd_ctx->session->kernel_session->trace_path); if (ret < 0) { ret = LTTCOMM_KERN_META_FAIL; goto error; @@ -2023,7 +2121,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) goto setup_error; } - DBG("Listing events (%ld events)", nb_event); + DBG("Listing events (%zu events)", nb_event); list_lttng_events(kchan, (struct lttng_event *)(cmd_ctx->llm->payload)); @@ -2056,7 +2154,6 @@ static int process_client_msg(struct command_ctx *cmd_ctx) ret = LTTCOMM_OK; break; } - case LTTNG_CALIBRATE: { /* Setup lttng message with no payload */ @@ -2086,6 +2183,43 @@ static int process_client_msg(struct command_ctx *cmd_ctx) ret = LTTCOMM_OK; break; } + case LTTNG_REGISTER_CONSUMER: + { + int sock; + + /* Setup lttng message with no payload */ + ret = setup_lttng_msg(cmd_ctx, 0); + if (ret < 0) { + goto setup_error; + } + + switch (cmd_ctx->lsm->domain.type) { + case LTTNG_DOMAIN_KERNEL: + { + /* Can't register a consumer if there is already one */ + if (cmd_ctx->session->kernel_session->consumer_fd != 0) { + ret = LTTCOMM_CONNECT_FAIL; + goto error; + } + + sock = lttcomm_connect_unix_sock(cmd_ctx->lsm->u.reg.path); + if (sock < 0) { + ret = LTTCOMM_CONNECT_FAIL; + goto error; + } + + cmd_ctx->session->kernel_session->consumer_fd = sock; + break; + } + default: + /* TODO: Userspace tracing */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + break; + } default: /* Undefined command */ @@ -2260,6 +2394,7 @@ static void usage(void) fprintf(stderr, " -S, --sig-parent Send SIGCHLD to parent pid to notify readiness.\n"); fprintf(stderr, " -q, --quiet No output at all.\n"); fprintf(stderr, " -v, --verbose Verbose mode. Activate DBG() macro.\n"); + fprintf(stderr, " --verbose-kconsumerd Verbose mode for kconsumerd. Activate DBG() macro.\n"); } /* @@ -2281,12 +2416,13 @@ static int parse_args(int argc, char **argv) { "version", 0, 0, 'V' }, { "quiet", 0, 0, 'q' }, { "verbose", 0, 0, 'v' }, + { "verbose-kconsumerd", 0, 0, 'Z' }, { NULL, 0, 0, 0 } }; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "dhqvVS" "a:c:g:s:E:C:", long_options, &option_index); + c = getopt_long(argc, argv, "dhqvVS" "a:c:g:s:E:C:Z", long_options, &option_index); if (c == -1) { break; } @@ -2332,6 +2468,9 @@ static int parse_args(int argc, char **argv) /* Verbose level can increase using multiple -v */ opt_verbose += 1; break; + case 'Z': + opt_verbose_kconsumerd += 1; + break; default: /* Unknown option or other error. * Error is printed by getopt, just return */ @@ -2347,7 +2486,7 @@ static int parse_args(int argc, char **argv) * apps_sock - The communication socket for all UST apps. * client_sock - The communication of the cli tool (lttng). */ -static int init_daemon_socket() +static int init_daemon_socket(void) { int ret = 0; mode_t old_umask; @@ -2392,18 +2531,19 @@ end: } /* - * Check if the global socket is available. If yes, error is returned. + * Check if the global socket is available, and if a daemon is answering + * at the other side. If yes, error is returned. */ -static int check_existing_daemon() +static int check_existing_daemon(void) { - int ret; - - ret = access(client_unix_sock_path, F_OK); - if (ret == 0) { - ret = access(apps_unix_sock_path, F_OK); - } - - return ret; + if (access(client_unix_sock_path, F_OK) < 0 && + access(apps_unix_sock_path, F_OK) < 0) + return 0; + /* Is there anybody out there ? */ + if (lttng_session_daemon_alive()) + return -EEXIST; + else + return 0; } /* @@ -2415,14 +2555,10 @@ static int check_existing_daemon() static int set_permissions(void) { int ret; - struct group *grp; - - /* Decide which group name to use */ - (opt_tracing_group != NULL) ? - (grp = getgrnam(opt_tracing_group)) : - (grp = getgrnam(default_tracing_group)); + gid_t gid; - if (grp == NULL) { + gid = allowed_group(); + if (gid < 0) { if (is_root) { WARN("No tracing group detected"); ret = 0; @@ -2434,21 +2570,21 @@ static int set_permissions(void) } /* Set lttng run dir */ - ret = chown(LTTNG_RUNDIR, 0, grp->gr_gid); + ret = chown(LTTNG_RUNDIR, 0, gid); if (ret < 0) { ERR("Unable to set group on " LTTNG_RUNDIR); perror("chown"); } /* lttng client socket path */ - ret = chown(client_unix_sock_path, 0, grp->gr_gid); + ret = chown(client_unix_sock_path, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", client_unix_sock_path); perror("chown"); } /* kconsumerd error socket path */ - ret = chown(kconsumerd_err_unix_sock_path, 0, grp->gr_gid); + ret = chown(kconsumerd_err_unix_sock_path, 0, gid); if (ret < 0) { ERR("Unable to set group on %s", kconsumerd_err_unix_sock_path); perror("chown"); @@ -2536,26 +2672,27 @@ error: /* * Signal handler for the daemon + * + * Simply stop all worker threads, leaving main() return gracefully + * after joining all threads and calling cleanup(). */ static void sighandler(int sig) { switch (sig) { - case SIGPIPE: - DBG("SIGPIPE catched"); - return; - case SIGINT: - DBG("SIGINT catched"); - cleanup(); - break; - case SIGTERM: - DBG("SIGTERM catched"); - cleanup(); - break; - default: - break; + case SIGPIPE: + DBG("SIGPIPE catched"); + return; + case SIGINT: + DBG("SIGINT catched"); + stop_threads(); + break; + case SIGTERM: + DBG("SIGTERM catched"); + stop_threads(); + break; + default: + break; } - - exit(EXIT_SUCCESS); } /* @@ -2625,14 +2762,14 @@ int main(int argc, char **argv) const char *home_path; /* Create thread quit pipe */ - if (init_thread_quit_pipe() < 0) { - goto exit; + if ((ret = init_thread_quit_pipe()) < 0) { + goto error; } /* Parse arguments */ progname = argv[0]; if ((ret = parse_args(argc, argv) < 0)) { - goto exit; + goto error; } /* Daemonize */ @@ -2640,7 +2777,7 @@ int main(int argc, char **argv) ret = daemon(0, 0); if (ret < 0) { perror("daemon"); - goto exit; + goto error; } } @@ -2650,7 +2787,7 @@ int main(int argc, char **argv) if (is_root) { ret = create_lttng_rundir(); if (ret < 0) { - goto exit; + goto error; } if (strlen(apps_unix_sock_path) == 0) { @@ -2667,7 +2804,8 @@ int main(int argc, char **argv) if (home_path == NULL) { /* TODO: Add --socket PATH option */ ERR("Can't get HOME directory for sockets creation."); - goto exit; + ret = -EPERM; + goto error; } if (strlen(apps_unix_sock_path) == 0) { @@ -2686,17 +2824,15 @@ int main(int argc, char **argv) DBG("Application socket path %s", apps_unix_sock_path); /* - * See if daemon already exist. If any of the two socket needed by the - * daemon are present, this test fails. However, if the daemon is killed - * with a SIGKILL, those unix socket must be unlinked by hand. + * See if daemon already exist. */ - if ((ret = check_existing_daemon()) == 0) { + if ((ret = check_existing_daemon()) < 0) { ERR("Already running daemon.\n"); /* - * We do not goto error because we must not cleanup() because a daemon - * is already running. + * We do not goto exit because we must not cleanup() + * because a daemon is already running. */ - goto exit; + goto error; } /* After this point, we can safely call cleanup() so goto error is used */ @@ -2710,7 +2846,7 @@ int main(int argc, char **argv) if (is_root) { ret = set_kconsumerd_sockets(); if (ret < 0) { - goto error; + goto exit; } /* Setup kernel tracer */ @@ -2720,18 +2856,18 @@ int main(int argc, char **argv) set_ulimit(); } - if (set_signal_handler() < 0) { - goto error; + if ((ret = set_signal_handler()) < 0) { + goto exit; } /* Setup the needed unix socket */ - if (init_daemon_socket() < 0) { - goto error; + if ((ret = init_daemon_socket()) < 0) { + goto exit; } /* Set credentials to socket */ - if (is_root && (set_permissions() < 0)) { - goto error; + if (is_root && ((ret = set_permissions()) < 0)) { + goto exit; } /* Get parent pid if -S, --sig-parent is specified. */ @@ -2740,8 +2876,8 @@ int main(int argc, char **argv) } /* Setup the kernel pipe for waking up the kernel thread */ - if (create_kernel_poll_pipe() < 0) { - goto error; + if ((ret = create_kernel_poll_pipe()) < 0) { + goto exit; } /* @@ -2750,41 +2886,61 @@ int main(int argc, char **argv) */ session_list_ptr = get_session_list(); - while (1) { - /* Create thread to manage the client socket */ - ret = pthread_create(&client_thread, NULL, thread_manage_clients, (void *) NULL); - if (ret != 0) { - perror("pthread_create"); - goto error; - } + /* Create thread to manage the client socket */ + ret = pthread_create(&client_thread, NULL, thread_manage_clients, (void *) NULL); + if (ret != 0) { + perror("pthread_create"); + goto exit_client; + } - /* Create thread to manage application socket */ - ret = pthread_create(&apps_thread, NULL, thread_manage_apps, (void *) NULL); - if (ret != 0) { - perror("pthread_create"); - goto error; - } + /* Create thread to manage application socket */ + ret = pthread_create(&apps_thread, NULL, thread_manage_apps, (void *) NULL); + if (ret != 0) { + perror("pthread_create"); + goto exit_apps; + } - /* Create kernel thread to manage kernel event */ - ret = pthread_create(&kernel_thread, NULL, thread_manage_kernel, (void *) NULL); - if (ret != 0) { - perror("pthread_create"); - goto error; - } + /* Create kernel thread to manage kernel event */ + ret = pthread_create(&kernel_thread, NULL, thread_manage_kernel, (void *) NULL); + if (ret != 0) { + perror("pthread_create"); + goto exit_kernel; + } - ret = pthread_join(client_thread, &status); - if (ret != 0) { - perror("pthread_join"); - goto error; - } + ret = pthread_join(kernel_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ } - cleanup(); - exit(EXIT_SUCCESS); +exit_kernel: + ret = pthread_join(apps_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ + } -error: - cleanup(); +exit_apps: + ret = pthread_join(client_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ + } + + ret = join_kconsumerd_thread(); + if (ret != 0) { + perror("join_kconsumerd"); + goto error; /* join error, exit without cleanup */ + } +exit_client: exit: + /* + * cleanup() is called when no other thread is running. + */ + cleanup(); + if (!ret) + exit(EXIT_SUCCESS); +error: exit(EXIT_FAILURE); }