| /* |
| * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
| * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. |
| * |
| * This file is part of LVM2. |
| * |
| * This copyrighted material is made available to anyone wishing to use, |
| * modify, copy, or redistribute it subject to the terms and conditions |
| * of the GNU Lesser General Public License v.2.1. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "lib.h" |
| #include "device.h" |
| #include "locking.h" |
| #include "lvm-exec.h" |
| #include "toolcontext.h" |
| |
| #include <unistd.h> |
| #include <sys/wait.h> |
| |
| /* |
| * Create verbose string with list of parameters |
| */ |
| static char *_verbose_args(const char *const argv[], char *buf, size_t sz) |
| { |
| int pos = 0; |
| int len; |
| unsigned i; |
| |
| buf[0] = '\0'; |
| for (i = 0; argv[i]; i++) { |
| if ((len = dm_snprintf(buf + pos, sz - pos, |
| " %s", argv[i])) < 0) |
| /* Truncated */ |
| break; |
| pos += len; |
| } |
| |
| return buf; |
| } |
| |
| /* |
| * Execute and wait for external command |
| */ |
| int exec_cmd(struct cmd_context *cmd, const char *const argv[], |
| int *rstatus, int sync_needed) |
| { |
| pid_t pid; |
| int status; |
| char buf[PATH_MAX * 2]; |
| |
| if (!argv[0]) { |
| log_error(INTERNAL_ERROR "Missing command."); |
| return 0; |
| } |
| |
| if (rstatus) |
| *rstatus = -1; |
| |
| if (sync_needed) |
| /* Flush ops and reset dm cookie */ |
| if (!sync_local_dev_names(cmd)) { |
| log_error("Failed to sync local device names before forking."); |
| return 0; |
| } |
| |
| log_verbose("Executing:%s", _verbose_args(argv, buf, sizeof(buf))); |
| |
| if ((pid = fork()) == -1) { |
| log_sys_error("fork", ""); |
| return 0; |
| } |
| |
| if (!pid) { |
| /* Child */ |
| reset_locking(); |
| dev_close_all(); |
| /* FIXME Fix effect of reset_locking on cache then include this */ |
| /* destroy_toolcontext(cmd); */ |
| /* FIXME Use execve directly */ |
| execvp(argv[0], (char **) argv); |
| log_sys_error("execvp", argv[0]); |
| _exit(errno); |
| } |
| |
| /* Parent */ |
| if (wait4(pid, &status, 0, NULL) != pid) { |
| log_error("wait4 child process %u failed: %s", pid, |
| strerror(errno)); |
| return 0; |
| } |
| |
| if (!WIFEXITED(status)) { |
| log_error("Child %u exited abnormally", pid); |
| return 0; |
| } |
| |
| if (WEXITSTATUS(status)) { |
| if (rstatus) { |
| *rstatus = WEXITSTATUS(status); |
| log_verbose("%s failed: %u", argv[0], *rstatus); |
| } else |
| log_error("%s failed: %u", argv[0], WEXITSTATUS(status)); |
| return 0; |
| } |
| |
| if (rstatus) |
| *rstatus = 0; |
| |
| return 1; |
| } |
| |
| static int _reopen_fd_to_null(int fd) |
| { |
| int null_fd; |
| int r = 0; |
| |
| if ((null_fd = open("/dev/null", O_RDWR)) == -1) { |
| log_sys_error("open", "/dev/null"); |
| return 0; |
| } |
| |
| if (close(fd)) { |
| log_sys_error("close", ""); |
| goto out; |
| } |
| |
| if (dup2(null_fd, fd) == -1) { |
| log_sys_error("dup2", ""); |
| goto out; |
| } |
| |
| r = 1; |
| out: |
| if (close(null_fd)) { |
| log_sys_error("dup2", ""); |
| return 0; |
| } |
| |
| return r; |
| } |
| |
| FILE *pipe_open(struct cmd_context *cmd, const char *const argv[], |
| int sync_needed, struct pipe_data *pdata) |
| { |
| int pipefd[2]; |
| char buf[PATH_MAX * 2]; |
| |
| if (sync_needed) |
| /* Flush ops and reset dm cookie */ |
| if (!sync_local_dev_names(cmd)) { |
| log_error("Failed to sync local device names before forking."); |
| return 0; |
| } |
| |
| if (pipe(pipefd)) { |
| log_sys_error("pipe", ""); |
| return 0; |
| } |
| |
| log_verbose("Piping:%s", _verbose_args(argv, buf, sizeof(buf))); |
| |
| if ((pdata->pid = fork()) == -1) { |
| log_sys_error("pipe", ""); |
| return 0; |
| } |
| |
| if (pdata->pid == 0) { |
| /* Child -> writer, convert pipe[0] to STDOUT */ |
| if (!_reopen_fd_to_null(STDIN_FILENO)) |
| stack; |
| else if (close(pipefd[0 /*read*/])) |
| log_sys_error("close", "pipe[0]"); |
| else if (close(STDOUT_FILENO)) |
| log_sys_error("close", "STDOUT"); |
| else if (dup2(pipefd[1 /*write*/], STDOUT_FILENO) == -1) |
| log_sys_error("dup2", "STDOUT"); |
| else if (close(pipefd[1])) |
| log_sys_error("close", "pipe[1]"); |
| else if (argv[0]) { |
| execvp(argv[0], (char **) argv); |
| log_sys_error("execvp", argv[0]); |
| } |
| _exit(errno); |
| } |
| |
| /* Parent -> reader */ |
| if (close(pipefd[1 /*write*/])) { |
| log_sys_error("close", "STDOUT"); |
| return NULL; |
| } |
| |
| if (!(pdata->fp = fdopen(pipefd[0 /*read*/], "r"))) { |
| log_sys_error("fdopen", "STDIN"); |
| if (close(pipefd[0])) |
| log_sys_error("close", "STDIN"); |
| return NULL; /* FIXME: kill */ |
| } |
| |
| return pdata->fp; |
| } |
| |
| int pipe_close(struct pipe_data *pdata) |
| { |
| int status; |
| |
| if (fclose(pdata->fp)) |
| log_sys_error("fclose", "STDIN"); |
| |
| if (waitpid(pdata->pid, &status, 0) != pdata->pid) { |
| log_sys_error("waitpid", ""); |
| return 0; |
| } |
| |
| return (status == 0) ? 1 : 0; |
| } |