Unverified Commit e4d234a2 by Christian Brauner Committed by Stéphane Graber

utils: fix lxc_popen()/lxc_pclose()

- rework and fix pipe fd leak Signed-off-by: 's avatarChristian Brauner <christian.brauner@ubuntu.com>
parent 4443ee50
...@@ -470,152 +470,105 @@ const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip) ...@@ -470,152 +470,105 @@ const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip)
return (const char**)lxc_va_arg_list_to_argv(ap, skip, 0); return (const char**)lxc_va_arg_list_to_argv(ap, skip, 0);
} }
extern struct lxc_popen_FILE *lxc_popen(const char *command) struct lxc_popen_FILE *lxc_popen(const char *command)
{ {
int ret; int ret;
struct lxc_popen_FILE *fp = NULL;
int parent_end = -1, child_end = -1;
int pipe_fds[2]; int pipe_fds[2];
pid_t child_pid; pid_t child_pid;
struct lxc_popen_FILE *fp = NULL;
int r = pipe2(pipe_fds, O_CLOEXEC); ret = pipe2(pipe_fds, O_CLOEXEC);
if (ret < 0)
if (r < 0) {
ERROR("pipe2 failure");
return NULL; return NULL;
}
parent_end = pipe_fds[0];
child_end = pipe_fds[1];
child_pid = fork(); child_pid = fork();
if (child_pid < 0)
goto on_error;
if (child_pid == 0) { if (!child_pid) {
/* child */ sigset_t mask;
close(parent_end);
if (child_end != STDOUT_FILENO) { close(pipe_fds[0]);
/* dup2() doesn't dup close-on-exec flag */
ret = dup2(child_end, STDOUT_FILENO);
if (ret < 0)
WARN("Failed to duplicate stdout fd");
} else {
/*
* The descriptor is already the one we will use.
* But it must not be marked close-on-exec.
* Undo the effects.
*/
ret = fcntl(child_end, F_SETFD, 0);
if (ret < 0) {
SYSERROR("Failed to remove FD_CLOEXEC from fd.");
exit(127);
}
}
if (child_end != STDERR_FILENO) { /* duplicate stdout */
/* dup2() doesn't dup close-on-exec flag */ if (pipe_fds[1] != STDOUT_FILENO)
ret = dup2(child_end, STDERR_FILENO); ret = dup2(pipe_fds[1], STDOUT_FILENO);
if (ret < 0) else
WARN("Failed to duplicate stdout fd"); ret = fcntl(pipe_fds[1], F_SETFD, 0);
} else { if (ret < 0) {
/* close(pipe_fds[1]);
* The descriptor is already the one we will use. exit(EXIT_FAILURE);
* But it must not be marked close-on-exec.
* Undo the effects.
*/
ret = fcntl(child_end, F_SETFD, 0);
if (ret < 0) {
SYSERROR("Failed to remove FD_CLOEXEC from fd.");
exit(127);
}
}
/*
* Unblock signals.
* This is the main/only reason
* why we do our lousy popen() emulation.
*/
{
sigset_t mask;
sigfillset(&mask);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
} }
execl("/bin/sh", "sh", "-c", command, (char *) NULL); /* duplicate stderr */
exit(127); if (pipe_fds[1] != STDERR_FILENO)
} ret = dup2(pipe_fds[1], STDERR_FILENO);
else
ret = fcntl(pipe_fds[1], F_SETFD, 0);
close(pipe_fds[1]);
if (ret < 0)
exit(EXIT_FAILURE);
/* parent */ /* unblock all signals */
ret = sigfillset(&mask);
if (ret < 0)
exit(EXIT_FAILURE);
close(child_end); ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
if (ret < 0)
exit(EXIT_FAILURE);
if (child_pid < 0) { execl("/bin/sh", "sh", "-c", command, (char *)NULL);
ERROR("fork failure"); exit(127);
goto error;
} }
fp = calloc(1, sizeof(*fp)); close(pipe_fds[1]);
if (!fp) { pipe_fds[1] = -1;
ERROR("failed to allocate memory");
goto error;
}
fp->f = fdopen(parent_end, "r"); fp = malloc(sizeof(*fp));
if (!fp->f) { if (!fp)
ERROR("fdopen failure"); goto on_error;
goto error;
}
fp->child_pid = child_pid; fp->child_pid = child_pid;
fp->pipe = pipe_fds[0];
return fp; fp->f = fdopen(pipe_fds[0], "r");
if (!fp->f)
error: goto on_error;
if (fp) { return fp;
if (fp->f) {
fclose(fp->f);
parent_end = -1; /* so we do not close it second time */
}
on_error:
if (fp)
free(fp); free(fp);
}
if (parent_end != -1) if (pipe_fds[0] >= 0)
close(parent_end); close(pipe_fds[0]);
if (pipe_fds[1] >= 0)
close(pipe_fds[1]);
return NULL; return NULL;
} }
extern int lxc_pclose(struct lxc_popen_FILE *fp) int lxc_pclose(struct lxc_popen_FILE *fp)
{ {
FILE *f = NULL;
pid_t child_pid = 0;
int wstatus = 0;
pid_t wait_pid; pid_t wait_pid;
int wstatus = 0;
if (fp) { if (!fp)
f = fp->f;
child_pid = fp->child_pid;
/* free memory (we still need to close file stream) */
free(fp);
fp = NULL;
}
if (!f || fclose(f)) {
ERROR("fclose failure");
return -1; return -1;
}
do { do {
wait_pid = waitpid(child_pid, &wstatus, 0); wait_pid = waitpid(fp->child_pid, &wstatus, 0);
} while (wait_pid == -1 && errno == EINTR); } while (wait_pid < 0 && errno == EINTR);
close(fp->pipe);
fclose(fp->f);
free(fp);
if (wait_pid == -1) { if (wait_pid < 0)
ERROR("waitpid failure");
return -1; return -1;
}
return wstatus; return wstatus;
} }
......
...@@ -190,6 +190,7 @@ static inline int signalfd(int fd, const sigset_t *mask, int flags) ...@@ -190,6 +190,7 @@ static inline int signalfd(int fd, const sigset_t *mask, int flags)
* without additional wrappers. * without additional wrappers.
*/ */
struct lxc_popen_FILE { struct lxc_popen_FILE {
int pipe;
FILE *f; FILE *f;
pid_t child_pid; pid_t child_pid;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment