• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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