blob: f0647e29f5c419e2535c63ad1e12c6f00fff6ade [file] [log] [blame]
Darin Petkov478435e2011-05-17 11:46:31 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/subprocess.h"
Darin Petkova0b9e772011-10-06 05:05:56 -07006
adlr@google.com3defe6a2009-12-04 20:57:17 +00007#include <stdlib.h>
8#include <string.h>
Kenneth Watersa7fcafa2010-09-21 10:27:03 -07009#include <unistd.h>
Darin Petkova0b9e772011-10-06 05:05:56 -070010
11#include <string>
adlr@google.com3defe6a2009-12-04 20:57:17 +000012#include <vector>
Darin Petkova0b9e772011-10-06 05:05:56 -070013
14#include <base/logging.h>
15#include <base/memory/scoped_ptr.h>
16#include <base/string_util.h>
17
18#include "update_engine/utils.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000019
20using std::string;
Darin Petkov6f03a3b2010-11-10 14:27:14 -080021using std::tr1::shared_ptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +000022using std::vector;
23
24namespace chromeos_update_engine {
25
26void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080027 SubprocessRecord* record = reinterpret_cast<SubprocessRecord*>(data);
28
29 // Make sure we read any remaining process output. Then close the pipe.
30 GStdoutWatchCallback(record->gioout, G_IO_IN, &record->stdout);
31 int fd = g_io_channel_unix_get_fd(record->gioout);
32 g_source_remove(record->gioout_tag);
33 g_io_channel_unref(record->gioout);
34 close(fd);
35
adlr@google.com3defe6a2009-12-04 20:57:17 +000036 g_spawn_close_pid(pid);
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070037 gint use_status = status;
38 if (WIFEXITED(status))
39 use_status = WEXITSTATUS(status);
40
Darin Petkov6f03a3b2010-11-10 14:27:14 -080041 if (status) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070042 LOG(INFO) << "Subprocess status: " << use_status;
Darin Petkov6f03a3b2010-11-10 14:27:14 -080043 }
44 if (!record->stdout.empty()) {
45 LOG(INFO) << "Subprocess output:\n" << record->stdout;
46 }
47 if (record->callback) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070048 record->callback(use_status, record->stdout, record->callback_data);
Darin Petkov6f03a3b2010-11-10 14:27:14 -080049 }
50 Get().subprocess_records_.erase(record->tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +000051}
52
Kenneth Watersa7fcafa2010-09-21 10:27:03 -070053void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
54 dup2(1, 2);
55}
56
Darin Petkov6f03a3b2010-11-10 14:27:14 -080057gboolean Subprocess::GStdoutWatchCallback(GIOChannel* source,
58 GIOCondition condition,
59 gpointer data) {
60 string* stdout = reinterpret_cast<string*>(data);
61 char buf[1024];
62 gsize bytes_read;
63 while (g_io_channel_read_chars(source,
64 buf,
65 arraysize(buf),
66 &bytes_read,
67 NULL) == G_IO_STATUS_NORMAL &&
68 bytes_read > 0) {
69 stdout->append(buf, bytes_read);
70 }
71 return TRUE; // Keep the callback source. It's freed in GChilExitedCallback.
72}
73
adlr@google.com3defe6a2009-12-04 20:57:17 +000074namespace {
75void FreeArgv(char** argv) {
76 for (int i = 0; argv[i]; i++) {
77 free(argv[i]);
78 argv[i] = NULL;
79 }
80}
Andrew de los Reyes3270f742010-07-15 22:28:14 -070081
Chris Masonec6c57a52010-09-23 13:06:14 -070082void FreeArgvInError(char** argv) {
83 FreeArgv(argv);
84 LOG(ERROR) << "Ran out of memory copying args.";
85}
86
Andrew de los Reyes3270f742010-07-15 22:28:14 -070087// Note: Caller responsible for free()ing the returned value!
Chris Masonec6c57a52010-09-23 13:06:14 -070088// Will return NULL on failure and free any allocated memory.
Andrew de los Reyes3270f742010-07-15 22:28:14 -070089char** ArgPointer() {
90 const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
91 char** ret = new char*[arraysize(keys) + 1];
92 int pointer = 0;
93 for (size_t i = 0; i < arraysize(keys); i++) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070094 if (getenv(keys[i])) {
95 ret[pointer] = strdup(StringPrintf("%s=%s", keys[i],
96 getenv(keys[i])).c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -070097 if (!ret[pointer]) {
98 FreeArgv(ret);
99 delete [] ret;
100 return NULL;
101 }
102 ++pointer;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700103 }
104 }
Chris Masonec6c57a52010-09-23 13:06:14 -0700105 ret[pointer] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700106 return ret;
107}
108
109class ScopedFreeArgPointer {
110 public:
111 ScopedFreeArgPointer(char** arr) : arr_(arr) {}
112 ~ScopedFreeArgPointer() {
113 if (!arr_)
114 return;
115 for (int i = 0; arr_[i]; i++)
116 free(arr_[i]);
117 delete[] arr_;
118 }
119 private:
120 char** arr_;
121 DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
122};
adlr@google.com3defe6a2009-12-04 20:57:17 +0000123} // namespace {}
124
Darin Petkov85d02b72011-05-17 13:25:51 -0700125uint32_t Subprocess::Exec(const vector<string>& cmd,
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700126 ExecCallback callback,
127 void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000128 GPid child_pid;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700129 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000130 for (unsigned int i = 0; i < cmd.size(); i++) {
131 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700132 if (!argv[i]) {
133 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
134 return 0;
135 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000136 }
137 argv[cmd.size()] = NULL;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700138
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700139 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700140 if (!argp) {
141 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
142 return 0;
143 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700144 ScopedFreeArgPointer argp_free(argp);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000145
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800146 shared_ptr<SubprocessRecord> record(new SubprocessRecord);
147 record->callback = callback;
148 record->callback_data = p;
149 gint stdout_fd = -1;
150 bool success = g_spawn_async_with_pipes(
151 NULL, // working directory
152 argv.get(),
153 argp,
154 G_SPAWN_DO_NOT_REAP_CHILD, // flags
155 GRedirectStderrToStdout, // child setup function
156 NULL, // child setup data pointer
157 &child_pid,
158 NULL,
159 &stdout_fd,
160 NULL,
Darin Petkov478435e2011-05-17 11:46:31 -0700161 NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000162 FreeArgv(argv.get());
163 if (!success) {
164 LOG(ERROR) << "g_spawn_async failed";
165 return 0;
166 }
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800167 record->tag =
168 g_child_watch_add(child_pid, GChildExitedCallback, record.get());
169 subprocess_records_[record->tag] = record;
170
171 // Capture the subprocess output.
172 record->gioout = g_io_channel_unix_new(stdout_fd);
173 g_io_channel_set_encoding(record->gioout, NULL, NULL);
174 LOG_IF(WARNING,
175 g_io_channel_set_flags(record->gioout, G_IO_FLAG_NONBLOCK, NULL) !=
176 G_IO_STATUS_NORMAL) << "Unable to set non-blocking I/O mode.";
177 record->gioout_tag = g_io_add_watch(
178 record->gioout,
179 static_cast<GIOCondition>(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
180 GStdoutWatchCallback,
181 &record->stdout);
182 return record->tag;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000183}
184
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700185void Subprocess::CancelExec(uint32_t tag) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800186 subprocess_records_[tag]->callback = NULL;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000187}
188
Darin Petkov85d02b72011-05-17 13:25:51 -0700189bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
190 GSpawnFlags flags,
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700191 int* return_code,
Darin Petkov85d02b72011-05-17 13:25:51 -0700192 string* stdout) {
193 if (stdout) {
194 *stdout = "";
195 }
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700196 GError* err = NULL;
197 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000198 for (unsigned int i = 0; i < cmd.size(); i++) {
199 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700200 if (!argv[i]) {
201 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
202 return false;
203 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000204 }
205 argv[cmd.size()] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700206
207 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700208 if (!argp) {
209 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
210 return false;
211 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700212 ScopedFreeArgPointer argp_free(argp);
213
214 char* child_stdout;
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700215 bool success = g_spawn_sync(
216 NULL, // working directory
217 argv.get(),
218 argp,
Andrew de los Reyes50f36492010-11-01 13:57:12 -0700219 static_cast<GSpawnFlags>(G_SPAWN_STDERR_TO_DEV_NULL |
220 G_SPAWN_SEARCH_PATH | flags), // flags
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700221 GRedirectStderrToStdout, // child setup function
222 NULL, // data for child setup function
223 &child_stdout,
224 NULL,
225 return_code,
226 &err);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000227 FreeArgv(argv.get());
Darin Petkova0b9e772011-10-06 05:05:56 -0700228 LOG_IF(INFO, err) << utils::GetAndFreeGError(&err);
Darin Petkov478435e2011-05-17 11:46:31 -0700229 if (child_stdout) {
Darin Petkov85d02b72011-05-17 13:25:51 -0700230 if (stdout) {
231 *stdout = child_stdout;
232 } else if (*child_stdout) {
Darin Petkov478435e2011-05-17 11:46:31 -0700233 LOG(INFO) << "Subprocess output:\n" << child_stdout;
234 }
235 g_free(child_stdout);
236 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000237 return success;
238}
239
Darin Petkov85d02b72011-05-17 13:25:51 -0700240bool Subprocess::SynchronousExec(const std::vector<std::string>& cmd,
241 int* return_code,
242 std::string* stdout) {
243 return SynchronousExecFlags(cmd,
244 static_cast<GSpawnFlags>(0),
245 return_code,
246 stdout);
247}
248
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800249bool Subprocess::SubprocessInFlight() {
250 for (std::map<int, shared_ptr<SubprocessRecord> >::iterator it =
251 subprocess_records_.begin();
252 it != subprocess_records_.end(); ++it) {
253 if (it->second->callback)
254 return true;
255 }
256 return false;
257}
258
adlr@google.com3defe6a2009-12-04 20:57:17 +0000259Subprocess* Subprocess::subprocess_singleton_ = NULL;
260
261} // namespace chromeos_update_engine