diff options
-rw-r--r-- | inc/abrtlib.h | 26 | ||||
-rw-r--r-- | lib/Plugins/CCpp.cpp | 70 | ||||
-rw-r--r-- | lib/Plugins/RunApp.cpp | 32 | ||||
-rw-r--r-- | lib/Plugins/SOSreport.cpp | 4 | ||||
-rw-r--r-- | lib/Utils/Makefile.am | 2 | ||||
-rw-r--r-- | lib/Utils/popen_and_save_output.cpp | 30 | ||||
-rw-r--r-- | lib/Utils/spawn.cpp | 121 | ||||
-rw-r--r-- | lib/Utils/xfuncs.cpp | 6 |
8 files changed, 186 insertions, 105 deletions
diff --git a/inc/abrtlib.h b/inc/abrtlib.h index 28140d80..fadd7f18 100644 --- a/inc/abrtlib.h +++ b/inc/abrtlib.h @@ -132,6 +132,7 @@ int xsocket(int domain, int type, int protocol); void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen); void xlisten(int s, int backlog); ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, socklen_t tolen); +void xchdir(const char *path); void xstat(const char *name, struct stat *stat_buf); /* Just testing dent->d_type == DT_REG is wrong: some filesystems * do not report the type, they report DT_UNKNOWN for every dirent @@ -161,6 +162,30 @@ off_t copy_file(const char *src_name, const char *dst_name); void xsetreuid(uid_t ruid, uid_t euid); void xsetregid(gid_t rgid, uid_t egid); +enum { + EXECFLG_INPUT = 1 << 0, + EXECFLG_OUTPUT = 1 << 1, + EXECFLG_INPUT_NUL = 1 << 2, + EXECFLG_OUTPUT_NUL = 1 << 3, + EXECFLG_ERR2OUT = 1 << 4, + EXECFLG_ERR_NUL = 1 << 5, + EXECFLG_QUIET = 1 << 6, + EXECFLG_SETGUID = 1 << 7, + EXECFLG_SETSID = 1 << 8, +}; +/* Returns pid */ +pid_t fork_execv_on_steroids(int flags, + char **argv, + int *pipefds, + char **unsetenv_vec, + const char *dir, + uid_t uid); +/* Returns malloc'ed string. NULs are retained, and extra one is appended + * after the last byte (this NUL is not accounted for in *size_p) */ +char *run_in_shell_and_save_output(int flags, + const char *cmd, + const char *dir, + size_t *size_p); unsigned long long monotonic_ns(void); @@ -246,7 +271,6 @@ to_string(T x) return o.str(); } -std::string popen_and_save_output(const char *cmd); void parse_args(const char *psArgs, vector_string_t& pArgs, int quote = -1); void parse_release(const char *pRelease, std::string& pProduct, std::string& pVersion); diff --git a/lib/Plugins/CCpp.cpp b/lib/Plugins/CCpp.cpp index 1a01c010..26562beb 100644 --- a/lib/Plugins/CCpp.cpp +++ b/lib/Plugins/CCpp.cpp @@ -99,54 +99,28 @@ static string concat_str_vector(char **strings) /* Returns status. See `man 2 wait` for status information. */ static int ExecVP(char **pArgs, uid_t uid, int redirect_stderr, string& pOutput) { - int pipeout[2]; - pid_t child; - - xpipe(pipeout); - child = fork(); - if (child == -1) - { - perror_msg_and_die("fork"); - } - if (child == 0) - { - VERB1 log("Executing: %s", concat_str_vector(pArgs).c_str()); - close(pipeout[0]); /* read side of the pipe */ - xmove_fd(pipeout[1], STDOUT_FILENO); - /* Make sure stdin is safely open to nothing */ - xmove_fd(xopen("/dev/null", O_RDONLY), STDIN_FILENO); - - struct passwd* pw = getpwuid(uid); - gid_t gid = pw ? pw->pw_gid : uid; - setgroups(1, &gid); - xsetregid(gid, gid); - xsetreuid(uid, uid); - setsid(); - - /* Nuke everything which may make setlocale() switch to non-POSIX locale: - * we need to avoid having gdb output in some obscure language. - */ - unsetenv("LANG"); - unsetenv("LC_ALL"); - unsetenv("LC_COLLATE"); - unsetenv("LC_CTYPE"); - unsetenv("LC_MESSAGES"); - unsetenv("LC_MONETARY"); - unsetenv("LC_NUMERIC"); - unsetenv("LC_TIME"); - - if (redirect_stderr) - { - /* We want parent to see errors in the same stream */ - xdup2(STDOUT_FILENO, STDERR_FILENO); - } - execvp(pArgs[0], pArgs); - /* VERB1 since sometimes we expect errors here */ - VERB1 perror_msg("Can't execute '%s'", pArgs[0]); - exit(1); - } + /* 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 unsetenv_vec[] = { + "LANG", + "LC_ALL", + "LC_COLLATE", + "LC_CTYPE", + "LC_MESSAGES", + "LC_MONETARY", + "LC_NUMERIC", + "LC_TIME", + NULL + }; + + int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETGUID | EXECFLG_SETSID | EXECFLG_QUIET; + if (redirect_stderr) + flags |= EXECFLG_ERR2OUT; + VERB1 flags &= ~EXECFLG_QUIET; - close(pipeout[1]); /* write side of the pipe */ + int pipeout[2]; + pid_t child = fork_execv_on_steroids(flags, pArgs, pipeout, (char**)unsetenv_vec, /*dir:*/ NULL, uid); int r; char buff[1024]; @@ -155,8 +129,8 @@ static int ExecVP(char **pArgs, uid_t uid, int redirect_stderr, string& pOutput) buff[r] = '\0'; pOutput += buff; } - close(pipeout[0]); + int status; waitpid(child, &status, 0); /* prevent having zombie child process */ diff --git a/lib/Plugins/RunApp.cpp b/lib/Plugins/RunApp.cpp index f7cbc018..c1f725be 100644 --- a/lib/Plugins/RunApp.cpp +++ b/lib/Plugins/RunApp.cpp @@ -33,7 +33,8 @@ using namespace std; void CActionRunApp::Run(const char *pActionDir, const char *pArgs) { - /* Don't update_client() - actions run at crash time */ + /* Don't update_client() - actions run at crash time, there is no client + * to talk to at that point */ log("RunApp('%s','%s')", pActionDir, pArgs); vector_string_t args; @@ -45,38 +46,21 @@ void CActionRunApp::Run(const char *pActionDir, const char *pArgs) return; } -//FIXME: need to be able to escape " in .conf - /* Chdir to the dump dir. Command can analyze component and such. + /* NB: we chdir to the dump dir. Command can analyze component and such. * Example: * test x"`cat component`" = x"xorg-x11-apps" && cp /var/log/Xorg.0.log . */ -//Can do it using chdir() in child if we'd open-code popen - string cd_and_cmd = ssprintf("cd '%s'; %s", pActionDir, cmd); - VERB1 log("RunApp: executing '%s'", cd_and_cmd.c_str()); - FILE *fp = popen(cd_and_cmd.c_str(), "r"); - if (fp == NULL) - { - /* Happens only on resource starvation (fork fails or out-of-mem) */ - return; - } - -//FIXME: RunApp("gzip -9 </var/log/Xorg.0.log", "Xorg.0.log.gz") fails -//since we mangle NULs. - string output; - char line[1024]; - while (fgets(line, 1024, fp) != NULL) - { - if (args.size() > FILENAME) - output += line; - } - pclose(fp); + size_t cmd_out_size; + char *cmd_out = run_in_shell_and_save_output(/*flags:*/ 0, cmd, pActionDir, &cmd_out_size); if (args.size() > FILENAME) { CDebugDump dd; dd.Open(pActionDir); - dd.SaveText(args[FILENAME].c_str(), output.c_str()); + dd.SaveBinary(args[FILENAME].c_str(), cmd_out, cmd_out_size); } + + free(cmd_out); } PLUGIN_INFO(ACTION, diff --git a/lib/Plugins/SOSreport.cpp b/lib/Plugins/SOSreport.cpp index 6f231659..899b446f 100644 --- a/lib/Plugins/SOSreport.cpp +++ b/lib/Plugins/SOSreport.cpp @@ -91,7 +91,9 @@ void CActionSOSreport::Run(const char *pActionDir, const char *pArgs) update_client(_("running sosreport: %s"), command.c_str()); std::string output = command; output += '\n'; - output += popen_and_save_output(command.c_str()); + char *command_out = run_in_shell_and_save_output(/*flags:*/ 0, command.c_str(), /*dir:*/ NULL, /*size_p:*/ NULL); + output += command_out; + free(command_out); update_client(_("done running sosreport")); std::string sosreport_filename = ParseFilename(output); diff --git a/lib/Utils/Makefile.am b/lib/Utils/Makefile.am index d5e9d98b..a944d977 100644 --- a/lib/Utils/Makefile.am +++ b/lib/Utils/Makefile.am @@ -16,7 +16,7 @@ libABRTUtils_la_SOURCES = \ daemon.cpp \ skip_whitespace.cpp \ xatonum.cpp \ - popen_and_save_output.cpp \ + spawn.cpp \ stringops.cpp \ dirsize.cpp \ DebugDump.h DebugDump.cpp \ diff --git a/lib/Utils/popen_and_save_output.cpp b/lib/Utils/popen_and_save_output.cpp deleted file mode 100644 index 4bcbcac4..00000000 --- a/lib/Utils/popen_and_save_output.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Utility routines. - * - * Licensed under GPLv2 or later, see file COPYING in this tarball for details. - */ -#include "abrtlib.h" - -using namespace std; - -string popen_and_save_output(const char *cmd) -{ - string result; - - FILE *fp = popen(cmd, "r"); - if (fp == NULL) /* fork or pipe failed; or out-of-mem */ - { - return result; - } - - size_t sz; - char buf[BUFSIZ + 1]; - while ((sz = fread(buf, 1, sizeof(buf)-1, fp)) > 0) - { - buf[sz] = '\0'; - result += buf; - } - pclose(fp); - - return result; -} diff --git a/lib/Utils/spawn.cpp b/lib/Utils/spawn.cpp new file mode 100644 index 00000000..4b0eecbf --- /dev/null +++ b/lib/Utils/spawn.cpp @@ -0,0 +1,121 @@ +/* + * Utility routines. + * + * Licensed under GPLv2, see file COPYING in this tarball for details. + */ +#include "abrtlib.h" + +/* Returns pid */ +pid_t fork_execv_on_steroids(int flags, + char **argv, + int *pipefds, + char **unsetenv_vec, + const char *dir, + uid_t uid) +{ + pid_t child; + /* Reminder: [0] is read end, [1] is write end */ + int pipe_to_child[2]; + int pipe_fm_child[2]; + + /* Sanitize flags */ + if (!pipefds) + flags &= ~(EXECFLG_INPUT | EXECFLG_OUTPUT); + + if (flags & EXECFLG_INPUT) + xpipe(pipe_to_child); + if (flags & EXECFLG_OUTPUT) + xpipe(pipe_fm_child); + + child = fork(); + if (child == -1) { + perror_msg_and_die("fork"); + } + if (child == 0) { + /* Child */ + + /* Play with stdio descriptors */ + if (flags & EXECFLG_INPUT) { + xmove_fd(pipe_to_child[0], STDIN_FILENO); + } else if (flags & EXECFLG_INPUT_NUL) { + xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO); + } + if (flags & EXECFLG_OUTPUT) { + xmove_fd(pipe_fm_child[1], STDOUT_FILENO); + } else if (flags & EXECFLG_OUTPUT_NUL) { + xmove_fd(xopen("/dev/null", O_RDWR), STDOUT_FILENO); + } + if (flags & EXECFLG_ERR2OUT) { + /* Want parent to see errors in the same stream */ + xdup2(STDOUT_FILENO, STDERR_FILENO); + } else if (flags & EXECFLG_ERR_NUL) { + xmove_fd(xopen("/dev/null", O_RDWR), STDERR_FILENO); + } + + if (flags & EXECFLG_SETGUID) { + struct passwd* pw = getpwuid(uid); + gid_t gid = pw ? pw->pw_gid : uid; + setgroups(1, &gid); + xsetregid(gid, gid); + xsetreuid(uid, uid); + } + if (flags & EXECFLG_SETSID) + setsid(); + + if (unsetenv_vec) { + while (*unsetenv_vec) + unsetenv(*unsetenv_vec++); + } + + if (dir) + xchdir(dir); + + execvp(argv[0], argv); + if (!(flags & EXECFLG_QUIET)) + perror_msg("Can't execute '%s'", argv[0]); + exit(127); /* shell uses this exitcode in this case */ + } + + if (flags & EXECFLG_INPUT) { + close(pipe_to_child[0]); + pipefds[1] = pipe_to_child[1]; + } + if (flags & EXECFLG_OUTPUT) { + close(pipe_fm_child[1]); + pipefds[0] = pipe_fm_child[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", "sh", "-c", cmd, NULL }; + int pipeout[2]; + pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout, + /*unsetenv_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; +} diff --git a/lib/Utils/xfuncs.cpp b/lib/Utils/xfuncs.cpp index 8621b5f4..3ab37396 100644 --- a/lib/Utils/xfuncs.cpp +++ b/lib/Utils/xfuncs.cpp @@ -141,6 +141,12 @@ off_t xlseek(int fd, off_t offset, int whence) return off; } +void xchdir(const char *path) +{ + if (chdir(path)) + perror_msg_and_die("chdir(%s)", path); +} + char* xvasprintf(const char *format, va_list p) { int r; |