blob: 9c1936708647d614e66eb81aad38bcde40a4bd60 [file] [log] [blame]
Chris Masonec6c57a52010-09-23 13:06:14 -07001// Copyright (c) 2009 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"
adlr@google.com3defe6a2009-12-04 20:57:17 +000012#include "base/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);
Darin Petkov6f03a3b2010-11-10 14:27:14 -080032 if (status) {
33 LOG(INFO) << "Subprocess status: " << status;
34 }
35 if (!record->stdout.empty()) {
36 LOG(INFO) << "Subprocess output:\n" << record->stdout;
37 }
38 if (record->callback) {
39 record->callback(status, record->stdout, record->callback_data);
40 }
41 Get().subprocess_records_.erase(record->tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +000042}
43
Kenneth Watersa7fcafa2010-09-21 10:27:03 -070044void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
45 dup2(1, 2);
46}
47
Darin Petkov6f03a3b2010-11-10 14:27:14 -080048gboolean Subprocess::GStdoutWatchCallback(GIOChannel* source,
49 GIOCondition condition,
50 gpointer data) {
51 string* stdout = reinterpret_cast<string*>(data);
52 char buf[1024];
53 gsize bytes_read;
54 while (g_io_channel_read_chars(source,
55 buf,
56 arraysize(buf),
57 &bytes_read,
58 NULL) == G_IO_STATUS_NORMAL &&
59 bytes_read > 0) {
60 stdout->append(buf, bytes_read);
61 }
62 return TRUE; // Keep the callback source. It's freed in GChilExitedCallback.
63}
64
adlr@google.com3defe6a2009-12-04 20:57:17 +000065namespace {
66void FreeArgv(char** argv) {
67 for (int i = 0; argv[i]; i++) {
68 free(argv[i]);
69 argv[i] = NULL;
70 }
71}
Andrew de los Reyes3270f742010-07-15 22:28:14 -070072
Chris Masonec6c57a52010-09-23 13:06:14 -070073void FreeArgvInError(char** argv) {
74 FreeArgv(argv);
75 LOG(ERROR) << "Ran out of memory copying args.";
76}
77
Andrew de los Reyes3270f742010-07-15 22:28:14 -070078// Note: Caller responsible for free()ing the returned value!
Chris Masonec6c57a52010-09-23 13:06:14 -070079// Will return NULL on failure and free any allocated memory.
Andrew de los Reyes3270f742010-07-15 22:28:14 -070080char** ArgPointer() {
81 const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
82 char** ret = new char*[arraysize(keys) + 1];
83 int pointer = 0;
84 for (size_t i = 0; i < arraysize(keys); i++) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070085 if (getenv(keys[i])) {
86 ret[pointer] = strdup(StringPrintf("%s=%s", keys[i],
87 getenv(keys[i])).c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -070088 if (!ret[pointer]) {
89 FreeArgv(ret);
90 delete [] ret;
91 return NULL;
92 }
93 ++pointer;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070094 }
95 }
Chris Masonec6c57a52010-09-23 13:06:14 -070096 ret[pointer] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070097 return ret;
98}
99
100class ScopedFreeArgPointer {
101 public:
102 ScopedFreeArgPointer(char** arr) : arr_(arr) {}
103 ~ScopedFreeArgPointer() {
104 if (!arr_)
105 return;
106 for (int i = 0; arr_[i]; i++)
107 free(arr_[i]);
108 delete[] arr_;
109 }
110 private:
111 char** arr_;
112 DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
113};
adlr@google.com3defe6a2009-12-04 20:57:17 +0000114} // namespace {}
115
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700116uint32_t Subprocess::Exec(const std::vector<std::string>& cmd,
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700117 ExecCallback callback,
118 void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000119 GPid child_pid;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700120 GError* err;
121 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000122 for (unsigned int i = 0; i < cmd.size(); i++) {
123 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700124 if (!argv[i]) {
125 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
126 return 0;
127 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000128 }
129 argv[cmd.size()] = NULL;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700130
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700131 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700132 if (!argp) {
133 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
134 return 0;
135 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700136 ScopedFreeArgPointer argp_free(argp);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000137
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800138 shared_ptr<SubprocessRecord> record(new SubprocessRecord);
139 record->callback = callback;
140 record->callback_data = p;
141 gint stdout_fd = -1;
142 bool success = g_spawn_async_with_pipes(
143 NULL, // working directory
144 argv.get(),
145 argp,
146 G_SPAWN_DO_NOT_REAP_CHILD, // flags
147 GRedirectStderrToStdout, // child setup function
148 NULL, // child setup data pointer
149 &child_pid,
150 NULL,
151 &stdout_fd,
152 NULL,
153 &err);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000154 FreeArgv(argv.get());
155 if (!success) {
156 LOG(ERROR) << "g_spawn_async failed";
157 return 0;
158 }
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800159 record->tag =
160 g_child_watch_add(child_pid, GChildExitedCallback, record.get());
161 subprocess_records_[record->tag] = record;
162
163 // Capture the subprocess output.
164 record->gioout = g_io_channel_unix_new(stdout_fd);
165 g_io_channel_set_encoding(record->gioout, NULL, NULL);
166 LOG_IF(WARNING,
167 g_io_channel_set_flags(record->gioout, G_IO_FLAG_NONBLOCK, NULL) !=
168 G_IO_STATUS_NORMAL) << "Unable to set non-blocking I/O mode.";
169 record->gioout_tag = g_io_add_watch(
170 record->gioout,
171 static_cast<GIOCondition>(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
172 GStdoutWatchCallback,
173 &record->stdout);
174 return record->tag;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000175}
176
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700177void Subprocess::CancelExec(uint32_t tag) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800178 subprocess_records_[tag]->callback = NULL;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000179}
180
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700181bool Subprocess::SynchronousExecFlags(const std::vector<std::string>& cmd,
182 int* return_code,
183 GSpawnFlags flags) {
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700184 GError* err = NULL;
185 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000186 for (unsigned int i = 0; i < cmd.size(); i++) {
187 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700188 if (!argv[i]) {
189 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
190 return false;
191 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000192 }
193 argv[cmd.size()] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700194
195 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700196 if (!argp) {
197 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
198 return false;
199 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700200 ScopedFreeArgPointer argp_free(argp);
201
202 char* child_stdout;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000203
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700204 bool success = g_spawn_sync(
205 NULL, // working directory
206 argv.get(),
207 argp,
Andrew de los Reyes50f36492010-11-01 13:57:12 -0700208 static_cast<GSpawnFlags>(G_SPAWN_STDERR_TO_DEV_NULL |
209 G_SPAWN_SEARCH_PATH | flags), // flags
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700210 GRedirectStderrToStdout, // child setup function
211 NULL, // data for child setup function
212 &child_stdout,
213 NULL,
214 return_code,
215 &err);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000216 FreeArgv(argv.get());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700217 if (err)
218 LOG(INFO) << "err is: " << err->code << ", " << err->message;
Andrew de los Reyesbef0c7d2010-08-20 10:20:10 -0700219 if (child_stdout && strlen(child_stdout))
Kenneth Watersa7fcafa2010-09-21 10:27:03 -0700220 LOG(INFO) << "Subprocess output:\n" << child_stdout;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000221 return success;
222}
223
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800224bool Subprocess::SubprocessInFlight() {
225 for (std::map<int, shared_ptr<SubprocessRecord> >::iterator it =
226 subprocess_records_.begin();
227 it != subprocess_records_.end(); ++it) {
228 if (it->second->callback)
229 return true;
230 }
231 return false;
232}
233
adlr@google.com3defe6a2009-12-04 20:57:17 +0000234Subprocess* Subprocess::subprocess_singleton_ = NULL;
235
236} // namespace chromeos_update_engine