summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--inc/abrtlib.h26
-rw-r--r--lib/Plugins/CCpp.cpp70
-rw-r--r--lib/Plugins/RunApp.cpp32
-rw-r--r--lib/Plugins/SOSreport.cpp4
-rw-r--r--lib/Utils/Makefile.am2
-rw-r--r--lib/Utils/popen_and_save_output.cpp30
-rw-r--r--lib/Utils/spawn.cpp121
-rw-r--r--lib/Utils/xfuncs.cpp6
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;