blob: a6381456db501397314eb02d4ccf5c002f74b604 [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"
6#include <stdlib.h>
7#include <string.h>
8#include <string>
Kenneth Watersa7fcafa2010-09-21 10:27:03 -07009#include <unistd.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000010#include <vector>
Chris Masone790e62e2010-08-12 10:41:18 -070011#include "base/logging.h"
Chris Masoned903c3b2011-05-12 15:35:46 -070012#include "base/memory/scoped_ptr.h"
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070013#include "base/string_util.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000014
15using std::string;
Darin Petkov6f03a3b2010-11-10 14:27:14 -080016using std::tr1::shared_ptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +000017using std::vector;
18
19namespace chromeos_update_engine {
20
21void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080022 SubprocessRecord* record = reinterpret_cast<SubprocessRecord*>(data);
23
24 // Make sure we read any remaining process output. Then close the pipe.
25 GStdoutWatchCallback(record->gioout, G_IO_IN, &record->stdout);
26 int fd = g_io_channel_unix_get_fd(record->gioout);
27 g_source_remove(record->gioout_tag);
28 g_io_channel_unref(record->gioout);
29 close(fd);
30
adlr@google.com3defe6a2009-12-04 20:57:17 +000031 g_spawn_close_pid(pid);
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070032 gint use_status = status;
33 if (WIFEXITED(status))
34 use_status = WEXITSTATUS(status);
35
Darin Petkov6f03a3b2010-11-10 14:27:14 -080036 if (status) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070037 LOG(INFO) << "Subprocess status: " << use_status;
Darin Petkov6f03a3b2010-11-10 14:27:14 -080038 }
39 if (!record->stdout.empty()) {
40 LOG(INFO) << "Subprocess output:\n" << record->stdout;
41 }
42 if (record->callback) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070043 record->callback(use_status, record->stdout, record->callback_data);
Darin Petkov6f03a3b2010-11-10 14:27:14 -080044 }
45 Get().subprocess_records_.erase(record->tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +000046}
47
Kenneth Watersa7fcafa2010-09-21 10:27:03 -070048void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
49 dup2(1, 2);
50}
51
Darin Petkov6f03a3b2010-11-10 14:27:14 -080052gboolean Subprocess::GStdoutWatchCallback(GIOChannel* source,
53 GIOCondition condition,
54 gpointer data) {
55 string* stdout = reinterpret_cast<string*>(data);
56 char buf[1024];
57 gsize bytes_read;
58 while (g_io_channel_read_chars(source,
59 buf,
60 arraysize(buf),
61 &bytes_read,
62 NULL) == G_IO_STATUS_NORMAL &&
63 bytes_read > 0) {
64 stdout->append(buf, bytes_read);
65 }
66 return TRUE; // Keep the callback source. It's freed in GChilExitedCallback.
67}
68
adlr@google.com3defe6a2009-12-04 20:57:17 +000069namespace {
70void FreeArgv(char** argv) {
71 for (int i = 0; argv[i]; i++) {
72 free(argv[i]);
73 argv[i] = NULL;
74 }
75}
Andrew de los Reyes3270f742010-07-15 22:28:14 -070076
Chris Masonec6c57a52010-09-23 13:06:14 -070077void FreeArgvInError(char** argv) {
78 FreeArgv(argv);
79 LOG(ERROR) << "Ran out of memory copying args.";
80}
81
Andrew de los Reyes3270f742010-07-15 22:28:14 -070082// Note: Caller responsible for free()ing the returned value!
Chris Masonec6c57a52010-09-23 13:06:14 -070083// Will return NULL on failure and free any allocated memory.
Andrew de los Reyes3270f742010-07-15 22:28:14 -070084char** ArgPointer() {
85 const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
86 char** ret = new char*[arraysize(keys) + 1];
87 int pointer = 0;
88 for (size_t i = 0; i < arraysize(keys); i++) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070089 if (getenv(keys[i])) {
90 ret[pointer] = strdup(StringPrintf("%s=%s", keys[i],
91 getenv(keys[i])).c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -070092 if (!ret[pointer]) {
93 FreeArgv(ret);
94 delete [] ret;
95 return NULL;
96 }
97 ++pointer;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070098 }
99 }
Chris Masonec6c57a52010-09-23 13:06:14 -0700100 ret[pointer] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700101 return ret;
102}
103
104class ScopedFreeArgPointer {
105 public:
106 ScopedFreeArgPointer(char** arr) : arr_(arr) {}
107 ~ScopedFreeArgPointer() {
108 if (!arr_)
109 return;
110 for (int i = 0; arr_[i]; i++)
111 free(arr_[i]);
112 delete[] arr_;
113 }
114 private:
115 char** arr_;
116 DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
117};
adlr@google.com3defe6a2009-12-04 20:57:17 +0000118} // namespace {}
119
Darin Petkov85d02b72011-05-17 13:25:51 -0700120uint32_t Subprocess::Exec(const vector<string>& cmd,
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700121 ExecCallback callback,
122 void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000123 GPid child_pid;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700124 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000125 for (unsigned int i = 0; i < cmd.size(); i++) {
126 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700127 if (!argv[i]) {
128 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
129 return 0;
130 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000131 }
132 argv[cmd.size()] = NULL;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700133
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700134 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700135 if (!argp) {
136 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
137 return 0;
138 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700139 ScopedFreeArgPointer argp_free(argp);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000140
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800141 shared_ptr<SubprocessRecord> record(new SubprocessRecord);
142 record->callback = callback;
143 record->callback_data = p;
144 gint stdout_fd = -1;
145 bool success = g_spawn_async_with_pipes(
146 NULL, // working directory
147 argv.get(),
148 argp,
149 G_SPAWN_DO_NOT_REAP_CHILD, // flags
150 GRedirectStderrToStdout, // child setup function
151 NULL, // child setup data pointer
152 &child_pid,
153 NULL,
154 &stdout_fd,
155 NULL,
Darin Petkov478435e2011-05-17 11:46:31 -0700156 NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000157 FreeArgv(argv.get());
158 if (!success) {
159 LOG(ERROR) << "g_spawn_async failed";
160 return 0;
161 }
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800162 record->tag =
163 g_child_watch_add(child_pid, GChildExitedCallback, record.get());
164 subprocess_records_[record->tag] = record;
165
166 // Capture the subprocess output.
167 record->gioout = g_io_channel_unix_new(stdout_fd);
168 g_io_channel_set_encoding(record->gioout, NULL, NULL);
169 LOG_IF(WARNING,
170 g_io_channel_set_flags(record->gioout, G_IO_FLAG_NONBLOCK, NULL) !=
171 G_IO_STATUS_NORMAL) << "Unable to set non-blocking I/O mode.";
172 record->gioout_tag = g_io_add_watch(
173 record->gioout,
174 static_cast<GIOCondition>(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
175 GStdoutWatchCallback,
176 &record->stdout);
177 return record->tag;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000178}
179
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700180void Subprocess::CancelExec(uint32_t tag) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800181 subprocess_records_[tag]->callback = NULL;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000182}
183
Darin Petkov85d02b72011-05-17 13:25:51 -0700184bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
185 GSpawnFlags flags,
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700186 int* return_code,
Darin Petkov85d02b72011-05-17 13:25:51 -0700187 string* stdout) {
188 if (stdout) {
189 *stdout = "";
190 }
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700191 GError* err = NULL;
192 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000193 for (unsigned int i = 0; i < cmd.size(); i++) {
194 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700195 if (!argv[i]) {
196 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
197 return false;
198 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000199 }
200 argv[cmd.size()] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700201
202 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700203 if (!argp) {
204 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
205 return false;
206 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700207 ScopedFreeArgPointer argp_free(argp);
208
209 char* child_stdout;
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700210 bool success = g_spawn_sync(
211 NULL, // working directory
212 argv.get(),
213 argp,
Andrew de los Reyes50f36492010-11-01 13:57:12 -0700214 static_cast<GSpawnFlags>(G_SPAWN_STDERR_TO_DEV_NULL |
215 G_SPAWN_SEARCH_PATH | flags), // flags
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700216 GRedirectStderrToStdout, // child setup function
217 NULL, // data for child setup function
218 &child_stdout,
219 NULL,
220 return_code,
221 &err);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000222 FreeArgv(argv.get());
Darin Petkov478435e2011-05-17 11:46:31 -0700223 if (err) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700224 LOG(INFO) << "err is: " << err->code << ", " << err->message;
Darin Petkov478435e2011-05-17 11:46:31 -0700225 g_error_free(err);
226 }
227 if (child_stdout) {
Darin Petkov85d02b72011-05-17 13:25:51 -0700228 if (stdout) {
229 *stdout = child_stdout;
230 } else if (*child_stdout) {
Darin Petkov478435e2011-05-17 11:46:31 -0700231 LOG(INFO) << "Subprocess output:\n" << child_stdout;
232 }
233 g_free(child_stdout);
234 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000235 return success;
236}
237
Darin Petkov85d02b72011-05-17 13:25:51 -0700238bool Subprocess::SynchronousExec(const std::vector<std::string>& cmd,
239 int* return_code,
240 std::string* stdout) {
241 return SynchronousExecFlags(cmd,
242 static_cast<GSpawnFlags>(0),
243 return_code,
244 stdout);
245}
246
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800247bool Subprocess::SubprocessInFlight() {
248 for (std::map<int, shared_ptr<SubprocessRecord> >::iterator it =
249 subprocess_records_.begin();
250 it != subprocess_records_.end(); ++it) {
251 if (it->second->callback)
252 return true;
253 }
254 return false;
255}
256
adlr@google.com3defe6a2009-12-04 20:57:17 +0000257Subprocess* Subprocess::subprocess_singleton_ = NULL;
258
259} // namespace chromeos_update_engine