static pid_t spawn_event_handler_child(const char *dump_dir_name, const char *event_name, int *fdp) { char *args[9]; args[0] = (char *) LIBEXEC_DIR"/abrt-handle-event"; /* Do not forward ASK_* messages to parent*/ args[1] = (char *) "-i"; args[2] = (char *) "--nice"; args[3] = (char *) "10"; args[4] = (char *) "-e"; args[5] = (char *) event_name; args[6] = (char *) "--"; args[7] = (char *) dump_dir_name; args[8] = NULL; int pipeout[2]; int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_QUIET | EXECFLG_ERR2OUT; VERB1 flags &= ~EXECFLG_QUIET; char *env_vec[3]; /* Intercept ASK_* messages in Client API -> don't wait for user response */ env_vec[0] = xstrdup("REPORT_CLIENT_NONINTERACTIVE=1"); env_vec[1] = xasprintf("%s=%d", ABRT_SERVER_EVENT_ENV, getpid()); env_vec[2] = NULL; pid_t child = fork_execv_on_steroids(flags, args, pipeout, env_vec, /*dir:*/ NULL, /*uid(unused):*/ 0); if (fdp) *fdp = pipeout[0]; return child; }
char *run_in_shell_and_save_output(int flags, const char *cmd, const char *dir, size_t *size_p) { flags |= EXECFLG_OUTPUT; flags &= ~EXECFLG_INPUT; const char *argv[] = { "/bin/sh", "-c", cmd, NULL }; int pipeout[2]; pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout, /*env_vec:*/ NULL, dir, /*uid (unused):*/ 0); size_t pos = 0; char *result = NULL; while (1) { result = (char*) xrealloc(result, pos + 4*1024 + 1); size_t sz = safe_read(pipeout[0], result + pos, 4*1024); if (sz <= 0) { break; } pos += sz; } result[pos] = '\0'; if (size_p) *size_p = pos; close(pipeout[0]); waitpid(child, NULL, 0); return result; }
int spawn_next_command(struct run_event_state *state, const char *dump_dir_name, const char *event, unsigned execflags ) { char *cmd = pop_next_command(&state->rule_list, NULL, /* don't return event_name */ NULL, /* NULL dd: we match by... */ dump_dir_name, /* ...dirname */ event, strlen(event)+1 /* for this event name exactly (not prefix) */ ); if (!cmd) return -1; /* We count it even if fork fails. The counter isn't meant * to count *successful* forks, it is meant to let caller know * whether the event we run has *any* handlers configured, or not. */ state->children_count++; log_notice("Executing '%s'", cmd); /* Export some useful environment variables for children */ char *env_vec[4]; /* Just exporting dump_dir_name isn't always ok: it can be "." * and some children want to cd to other directory but still * be able to find problem directory by using $DUMP_DIR... */ char *full_name = realpath(dump_dir_name, NULL); env_vec[0] = xasprintf("DUMP_DIR=%s", (full_name ? full_name : dump_dir_name)); free(full_name); env_vec[1] = xasprintf("EVENT=%s", event); env_vec[2] = xasprintf("REPORT_CLIENT_SLAVE=1"); env_vec[3] = NULL; char *argv[4]; argv[0] = (char*)"/bin/sh"; // TODO: honor $SHELL? argv[1] = (char*)"-c"; argv[2] = cmd; argv[3] = NULL; int pipefds[2]; state->command_pid = fork_execv_on_steroids( EXECFLG_INPUT | EXECFLG_OUTPUT | EXECFLG_ERR2OUT | execflags, argv, pipefds, /* env_vec: */ env_vec, /* dir: */ dump_dir_name, /* uid(unused): */ 0 ); state->command_out_fd = pipefds[0]; state->command_in_fd = pipefds[1]; free(env_vec[0]); free(env_vec[1]); free(env_vec[2]); free(cmd); return 0; }
static void exec_and_feed_input(const char* text, char **args) { int pipein[2]; pid_t child = fork_execv_on_steroids( EXECFLG_INPUT | EXECFLG_QUIET, args, pipein, /*env_vec:*/ NULL, /*dir:*/ NULL, /*uid (ignored):*/ 0 ); full_write_str(pipein[1], text); close(pipein[1]); int status; waitpid(child, &status, 0); /* wait for command completion */ if (status != 0) error_msg_and_die("Error running '%s'", args[0]); }
/** * * @param[out] status See `man 2 wait` for status information. * @return Malloc'ed string */ static char* exec_vp(char **args, int redirect_stderr, int exec_timeout_sec, int *status) { /* Nuke everything which may make setlocale() switch to non-POSIX locale: * we need to avoid having gdb output in some obscure language. */ static const char *const env_vec[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", /* Workaround for * http://sourceware.org/bugzilla/show_bug.cgi?id=9622 * (gdb emitting ESC sequences even with -batch) */ "TERM", NULL }; int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETSID | EXECFLG_QUIET; if (redirect_stderr) flags |= EXECFLG_ERR2OUT; VERB1 flags &= ~EXECFLG_QUIET; int pipeout[2]; pid_t child = fork_execv_on_steroids(flags, args, pipeout, (char**)env_vec, /*dir:*/ NULL, /*uid(unused):*/ 0); /* We use this function to run gdb and unstrip. Bugs in gdb or corrupted * coredumps were observed to cause gdb to enter infinite loop. * Therefore we have a (largish) timeout, after which we kill the child. */ ndelay_on(pipeout[0]); int t = time(NULL); /* int is enough, no need to use time_t */ int endtime = t + exec_timeout_sec; struct strbuf *buf_out = strbuf_new(); while (1) { int timeout = endtime - t; if (timeout < 0) { kill(child, SIGKILL); strbuf_append_strf(buf_out, "\n" "Timeout exceeded: %u seconds, killing %s.\n" "Looks like gdb hung while generating backtrace.\n" "This may be a bug in gdb. Consider submitting a bug report to gdb developers.\n" "Please attach coredump from this crash to the bug report if you do.\n", exec_timeout_sec, args[0] ); break; } /* We don't check poll result - checking read result is enough */ struct pollfd pfd; pfd.fd = pipeout[0]; pfd.events = POLLIN; poll(&pfd, 1, timeout * 1000); char buff[1024]; int r = read(pipeout[0], buff, sizeof(buff) - 1); if (r <= 0) { /* I did see EAGAIN happening here */ if (r < 0 && errno == EAGAIN) goto next; break; } buff[r] = '\0'; strbuf_append_str(buf_out, buff); next: t = time(NULL); } close(pipeout[0]); /* Prevent having zombie child process, and maybe collect status * (note that status == NULL is ok too) */ safe_waitpid(child, status, 0); return strbuf_free_nobuf(buf_out); }
char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec) { int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETSID | EXECFLG_QUIET; VERB1 flags &= ~EXECFLG_QUIET; int pipeout[2]; char* args[4]; args[0] = (char*)"eu-unstrip"; args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, dump_dir_name); args[2] = (char*)"-n"; args[3] = NULL; pid_t child = fork_execv_on_steroids(flags, args, pipeout, /*env_vec:*/ NULL, /*dir:*/ NULL, /*uid(unused):*/ 0); free(args[1]); /* Bugs in unstrip or corrupted coredumps can cause it to enter infinite loop. * Therefore we have a (largish) timeout, after which we kill the child. */ ndelay_on(pipeout[0]); int t = time(NULL); /* int is enough, no need to use time_t */ int endtime = t + timeout_sec; struct strbuf *buf_out = strbuf_new(); while (1) { int timeout = endtime - t; if (timeout < 0) { kill(child, SIGKILL); strbuf_free(buf_out); buf_out = NULL; break; } /* We don't check poll result - checking read result is enough */ struct pollfd pfd; pfd.fd = pipeout[0]; pfd.events = POLLIN; poll(&pfd, 1, timeout * 1000); char buff[1024]; int r = read(pipeout[0], buff, sizeof(buff) - 1); if (r <= 0) { /* I did see EAGAIN happening here */ if (r < 0 && errno == EAGAIN) goto next; break; } buff[r] = '\0'; strbuf_append_str(buf_out, buff); next: t = time(NULL); } close(pipeout[0]); /* Prevent having zombie child process */ int status; safe_waitpid(child, &status, 0); if (status != 0 || buf_out == NULL) { /* unstrip didnt exit with exit code 0, or we timed out */ strbuf_free(buf_out); return NULL; } return strbuf_free_nobuf(buf_out); }
static char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec) { struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) return NULL; char *uid_str = dd_load_text_ext(dd, FILENAME_UID, DD_FAIL_QUIETLY_ENOENT | DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE); dd_close(dd); uid_t uid = -1L; if (uid_str) { uid = xatoi_positive(uid_str); free(uid_str); if (uid == geteuid()) { uid = -1L; /* no need to setuid/gid if we are already under right uid */ } } int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETSID | EXECFLG_QUIET; if (uid != (uid_t)-1L) flags |= EXECFLG_SETGUID; VERB1 flags &= ~EXECFLG_QUIET; int pipeout[2]; char* args[4]; args[0] = (char*)"eu-unstrip"; args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, dump_dir_name); args[2] = (char*)"-n"; args[3] = NULL; pid_t child = fork_execv_on_steroids(flags, args, pipeout, /*env_vec:*/ NULL, /*dir:*/ NULL, uid); free(args[1]); /* Bugs in unstrip or corrupted coredumps can cause it to enter infinite loop. * Therefore we have a (largish) timeout, after which we kill the child. */ int t = time(NULL); /* int is enough, no need to use time_t */ int endtime = t + timeout_sec; struct strbuf *buf_out = strbuf_new(); while (1) { int timeout = endtime - t; if (timeout < 0) { kill(child, SIGKILL); strbuf_append_strf(buf_out, "\nTimeout exceeded: %u seconds, killing %s\n", timeout_sec, args[0]); break; } /* We don't check poll result - checking read result is enough */ struct pollfd pfd; pfd.fd = pipeout[0]; pfd.events = POLLIN; poll(&pfd, 1, timeout * 1000); char buff[1024]; int r = read(pipeout[0], buff, sizeof(buff) - 1); if (r <= 0) break; buff[r] = '\0'; strbuf_append_str(buf_out, buff); t = time(NULL); } close(pipeout[0]); /* Prevent having zombie child process */ int status; waitpid(child, &status, 0); if (status != 0) { /* unstrip didnt exit with exit code 0 */ strbuf_free(buf_out); return NULL; } return strbuf_free_nobuf(buf_out); }