1 // Copyright 2015 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "run_command.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <unistd.h>
13
14 #include <string>
15
16 #include "base/logging.h"
17
18 #include "compat/string.h"
19
20 namespace quipper {
21
22 namespace {
23
CloseFdOnExec(int fd)24 bool CloseFdOnExec(int fd) {
25 int fd_flags = fcntl(fd, F_GETFD);
26 if (fd_flags == -1) {
27 PLOG(ERROR) << "F_GETFD";
28 return false;
29 }
30 if (fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC)) {
31 PLOG(ERROR) << "F_SETFD FD_CLOEXEC";
32 return false;
33 }
34 return true;
35 }
36
ReadFromFd(int fd,std::vector<char> * output)37 void ReadFromFd(int fd, std::vector<char>* output) {
38 static const int kReadSize = 4096;
39 ssize_t read_sz;
40 size_t read_off = output->size();
41 do {
42 output->resize(read_off + kReadSize);
43 do {
44 read_sz = read(fd, output->data() + read_off, kReadSize);
45 } while (read_sz < 0 && errno == EINTR);
46 if (read_sz < 0) {
47 PLOG(FATAL) << "read";
48 break;
49 }
50 read_off += read_sz;
51 } while (read_sz > 0);
52 output->resize(read_off);
53 }
54
55 } // namespace
56
RunCommand(const std::vector<string> & command,std::vector<char> * output)57 int RunCommand(const std::vector<string>& command, std::vector<char>* output) {
58 std::vector<char*> c_str_cmd;
59 c_str_cmd.reserve(command.size() + 1);
60 for (const auto& c : command) {
61 // This cast is safe: POSIX states that exec shall not modify argv nor the
62 // strings pointed to by argv.
63 c_str_cmd.push_back(const_cast<char*>(c.c_str()));
64 }
65 c_str_cmd.push_back(nullptr);
66
67 // Create pipe for stdout:
68 int output_pipefd[2];
69 if (output) {
70 if (pipe(output_pipefd)) {
71 PLOG(ERROR) << "pipe";
72 return -1;
73 }
74 }
75
76 // Pipe for the child to return errno if exec fails:
77 int errno_pipefd[2];
78 if (pipe(errno_pipefd)) {
79 PLOG(ERROR) << "pipe for errno";
80 return -1;
81 }
82 if (!CloseFdOnExec(errno_pipefd[1])) return -1;
83
84 const pid_t child = fork();
85 if (child == 0) {
86 close(errno_pipefd[0]);
87
88 if (output) {
89 if (close(output_pipefd[0]) < 0) {
90 PLOG(FATAL) << "close read end of pipe";
91 }
92 }
93
94 int devnull_fd = open("/dev/null", O_WRONLY);
95 if (devnull_fd < 0) {
96 PLOG(FATAL) << "open /dev/null";
97 }
98
99 if (dup2(output ? output_pipefd[1] : devnull_fd, 1) < 0) {
100 PLOG(FATAL) << "dup2 stdout";
101 }
102
103 if (dup2(devnull_fd, 2) < 0) {
104 PLOG(FATAL) << "dup2 stderr";
105 }
106
107 if (close(devnull_fd) < 0) {
108 PLOG(FATAL) << "close /dev/null";
109 }
110
111 execvp(c_str_cmd[0], c_str_cmd.data());
112 int exec_errno = errno;
113
114 // exec failed... Write errno to a pipe so parent can retrieve it.
115 int ret;
116 do {
117 ret = write(errno_pipefd[1], &exec_errno, sizeof(exec_errno));
118 } while (ret < 0 && errno == EINTR);
119 close(errno_pipefd[1]);
120
121 std::_Exit(EXIT_FAILURE);
122 }
123
124 if (close(errno_pipefd[1])) {
125 PLOG(FATAL) << "close write end of errno pipe";
126 }
127 if (output) {
128 if (close(output_pipefd[1]) < 0) {
129 PLOG(FATAL) << "close write end of pipe";
130 }
131 }
132
133 // Check for errno:
134 int child_exec_errno;
135 int read_errno_res;
136 do {
137 read_errno_res =
138 read(errno_pipefd[0], &child_exec_errno, sizeof(child_exec_errno));
139 } while (read_errno_res < 0 && errno == EINTR);
140 if (read_errno_res < 0) {
141 PLOG(FATAL) << "read errno";
142 }
143 if (close(errno_pipefd[0])) {
144 PLOG(FATAL) << "close errno";
145 }
146
147 if (read_errno_res > 0) {
148 // exec failed in the child.
149 while (waitpid(child, nullptr, 0) < 0 && errno == EINTR) {
150 }
151 errno = child_exec_errno;
152 return -1;
153 }
154
155 // Read stdout from pipe.
156 if (output) {
157 ReadFromFd(output_pipefd[0], output);
158 if (close(output_pipefd[0])) {
159 PLOG(FATAL) << "close output";
160 }
161 }
162
163 // Wait for child.
164 int exit_status;
165 while (waitpid(child, &exit_status, 0) < 0 && errno == EINTR) {
166 }
167 errno = 0;
168 if (WIFEXITED(exit_status)) return WEXITSTATUS(exit_status);
169 return -1;
170 }
171
172 } // namespace quipper
173