blob: 253ba176ce6bbcd7a7d8302f27c4a2fd3cbf722f [file] [log] [blame]
Mike Frysinger8155d082012-04-06 15:23:18 -04001// Copyright (c) 2012 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
Ben Chan02f7c1d2014-10-18 15:18:02 -070011#include <memory>
Darin Petkova0b9e772011-10-06 05:05:56 -070012#include <string>
adlr@google.com3defe6a2009-12-04 20:57:17 +000013#include <vector>
Darin Petkova0b9e772011-10-06 05:05:56 -070014
15#include <base/logging.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070016#include <base/strings/string_util.h>
17#include <base/strings/stringprintf.h>
Darin Petkova0b9e772011-10-06 05:05:56 -070018
Alex Deymo44666f92014-07-22 20:29:24 -070019#include "update_engine/glib_utils.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000020
Alex Deymobc91a272014-05-20 16:45:33 -070021using std::shared_ptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +000022using std::string;
Ben Chan02f7c1d2014-10-18 15:18:02 -070023using std::unique_ptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +000024using std::vector;
25
26namespace chromeos_update_engine {
27
28void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080029 SubprocessRecord* record = reinterpret_cast<SubprocessRecord*>(data);
30
31 // Make sure we read any remaining process output. Then close the pipe.
32 GStdoutWatchCallback(record->gioout, G_IO_IN, &record->stdout);
33 int fd = g_io_channel_unix_get_fd(record->gioout);
34 g_source_remove(record->gioout_tag);
35 g_io_channel_unref(record->gioout);
36 close(fd);
37
adlr@google.com3defe6a2009-12-04 20:57:17 +000038 g_spawn_close_pid(pid);
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070039 gint use_status = status;
40 if (WIFEXITED(status))
41 use_status = WEXITSTATUS(status);
42
Darin Petkov6f03a3b2010-11-10 14:27:14 -080043 if (status) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070044 LOG(INFO) << "Subprocess status: " << use_status;
Darin Petkov6f03a3b2010-11-10 14:27:14 -080045 }
46 if (!record->stdout.empty()) {
47 LOG(INFO) << "Subprocess output:\n" << record->stdout;
48 }
49 if (record->callback) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070050 record->callback(use_status, record->stdout, record->callback_data);
Darin Petkov6f03a3b2010-11-10 14:27:14 -080051 }
52 Get().subprocess_records_.erase(record->tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +000053}
54
Kenneth Watersa7fcafa2010-09-21 10:27:03 -070055void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
56 dup2(1, 2);
57}
58
Darin Petkov6f03a3b2010-11-10 14:27:14 -080059gboolean Subprocess::GStdoutWatchCallback(GIOChannel* source,
60 GIOCondition condition,
61 gpointer data) {
62 string* stdout = reinterpret_cast<string*>(data);
63 char buf[1024];
64 gsize bytes_read;
65 while (g_io_channel_read_chars(source,
66 buf,
67 arraysize(buf),
68 &bytes_read,
Alex Vakulenko88b591f2014-08-28 16:48:57 -070069 nullptr) == G_IO_STATUS_NORMAL &&
Darin Petkov6f03a3b2010-11-10 14:27:14 -080070 bytes_read > 0) {
71 stdout->append(buf, bytes_read);
72 }
73 return TRUE; // Keep the callback source. It's freed in GChilExitedCallback.
74}
75
adlr@google.com3defe6a2009-12-04 20:57:17 +000076namespace {
77void FreeArgv(char** argv) {
78 for (int i = 0; argv[i]; i++) {
79 free(argv[i]);
Alex Vakulenko88b591f2014-08-28 16:48:57 -070080 argv[i] = nullptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +000081 }
82}
Andrew de los Reyes3270f742010-07-15 22:28:14 -070083
Chris Masonec6c57a52010-09-23 13:06:14 -070084void FreeArgvInError(char** argv) {
85 FreeArgv(argv);
86 LOG(ERROR) << "Ran out of memory copying args.";
87}
88
Andrew de los Reyes3270f742010-07-15 22:28:14 -070089// Note: Caller responsible for free()ing the returned value!
Alex Vakulenko88b591f2014-08-28 16:48:57 -070090// Will return null on failure and free any allocated memory.
Andrew de los Reyes3270f742010-07-15 22:28:14 -070091char** ArgPointer() {
92 const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
93 char** ret = new char*[arraysize(keys) + 1];
94 int pointer = 0;
95 for (size_t i = 0; i < arraysize(keys); i++) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070096 if (getenv(keys[i])) {
Alex Vakulenko75039d72014-03-25 12:36:28 -070097 ret[pointer] = strdup(base::StringPrintf("%s=%s", keys[i],
98 getenv(keys[i])).c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -070099 if (!ret[pointer]) {
100 FreeArgv(ret);
101 delete [] ret;
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700102 return nullptr;
Chris Masonec6c57a52010-09-23 13:06:14 -0700103 }
104 ++pointer;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700105 }
106 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700107 ret[pointer] = nullptr;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700108 return ret;
109}
110
111class ScopedFreeArgPointer {
112 public:
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700113 explicit ScopedFreeArgPointer(char** arr) : arr_(arr) {}
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700114 ~ScopedFreeArgPointer() {
115 if (!arr_)
116 return;
117 for (int i = 0; arr_[i]; i++)
118 free(arr_[i]);
119 delete[] arr_;
120 }
121 private:
122 char** arr_;
123 DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
124};
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700125} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +0000126
Darin Petkov85d02b72011-05-17 13:25:51 -0700127uint32_t Subprocess::Exec(const vector<string>& cmd,
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700128 ExecCallback callback,
129 void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000130 GPid child_pid;
Ben Chan02f7c1d2014-10-18 15:18:02 -0700131 unique_ptr<char*[]> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000132 for (unsigned int i = 0; i < cmd.size(); i++) {
133 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700134 if (!argv[i]) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700135 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700136 return 0;
137 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000138 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700139 argv[cmd.size()] = nullptr;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700140
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700141 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700142 if (!argp) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700143 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700144 return 0;
145 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700146 ScopedFreeArgPointer argp_free(argp);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000147
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800148 shared_ptr<SubprocessRecord> record(new SubprocessRecord);
149 record->callback = callback;
150 record->callback_data = p;
151 gint stdout_fd = -1;
Alex Deymo3e0b53e2014-08-12 23:12:25 -0700152 GError* error = nullptr;
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800153 bool success = g_spawn_async_with_pipes(
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700154 nullptr, // working directory
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800155 argv.get(),
156 argp,
157 G_SPAWN_DO_NOT_REAP_CHILD, // flags
158 GRedirectStderrToStdout, // child setup function
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700159 nullptr, // child setup data pointer
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800160 &child_pid,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700161 nullptr,
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800162 &stdout_fd,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700163 nullptr,
Alex Deymo3e0b53e2014-08-12 23:12:25 -0700164 &error);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000165 FreeArgv(argv.get());
166 if (!success) {
Alex Deymo3e0b53e2014-08-12 23:12:25 -0700167 LOG(ERROR) << "g_spawn_async failed: " << utils::GetAndFreeGError(&error);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000168 return 0;
169 }
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800170 record->tag =
171 g_child_watch_add(child_pid, GChildExitedCallback, record.get());
172 subprocess_records_[record->tag] = record;
173
174 // Capture the subprocess output.
175 record->gioout = g_io_channel_unix_new(stdout_fd);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700176 g_io_channel_set_encoding(record->gioout, nullptr, nullptr);
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800177 LOG_IF(WARNING,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700178 g_io_channel_set_flags(record->gioout, G_IO_FLAG_NONBLOCK, nullptr) !=
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800179 G_IO_STATUS_NORMAL) << "Unable to set non-blocking I/O mode.";
180 record->gioout_tag = g_io_add_watch(
181 record->gioout,
182 static_cast<GIOCondition>(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
183 GStdoutWatchCallback,
184 &record->stdout);
185 return record->tag;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000186}
187
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700188void Subprocess::CancelExec(uint32_t tag) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700189 subprocess_records_[tag]->callback = nullptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000190}
191
Darin Petkov85d02b72011-05-17 13:25:51 -0700192bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
193 GSpawnFlags flags,
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700194 int* return_code,
Darin Petkov85d02b72011-05-17 13:25:51 -0700195 string* stdout) {
196 if (stdout) {
197 *stdout = "";
198 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700199 GError* err = nullptr;
Ben Chan02f7c1d2014-10-18 15:18:02 -0700200 unique_ptr<char*[]> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000201 for (unsigned int i = 0; i < cmd.size(); i++) {
202 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700203 if (!argv[i]) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700204 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700205 return false;
206 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000207 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700208 argv[cmd.size()] = nullptr;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700209
210 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700211 if (!argp) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700212 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700213 return false;
214 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700215 ScopedFreeArgPointer argp_free(argp);
216
217 char* child_stdout;
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700218 bool success = g_spawn_sync(
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700219 nullptr, // working directory
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700220 argv.get(),
221 argp,
Andrew de los Reyes50f36492010-11-01 13:57:12 -0700222 static_cast<GSpawnFlags>(G_SPAWN_STDERR_TO_DEV_NULL |
223 G_SPAWN_SEARCH_PATH | flags), // flags
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700224 GRedirectStderrToStdout, // child setup function
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700225 nullptr, // data for child setup function
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700226 &child_stdout,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700227 nullptr,
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700228 return_code,
229 &err);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000230 FreeArgv(argv.get());
Darin Petkova0b9e772011-10-06 05:05:56 -0700231 LOG_IF(INFO, err) << utils::GetAndFreeGError(&err);
Darin Petkov478435e2011-05-17 11:46:31 -0700232 if (child_stdout) {
Darin Petkov85d02b72011-05-17 13:25:51 -0700233 if (stdout) {
234 *stdout = child_stdout;
235 } else if (*child_stdout) {
Darin Petkov478435e2011-05-17 11:46:31 -0700236 LOG(INFO) << "Subprocess output:\n" << child_stdout;
237 }
238 g_free(child_stdout);
239 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000240 return success;
241}
242
Darin Petkov85d02b72011-05-17 13:25:51 -0700243bool Subprocess::SynchronousExec(const std::vector<std::string>& cmd,
244 int* return_code,
245 std::string* stdout) {
246 return SynchronousExecFlags(cmd,
247 static_cast<GSpawnFlags>(0),
248 return_code,
249 stdout);
250}
251
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800252bool Subprocess::SubprocessInFlight() {
Ben Chanf9cb98c2014-09-21 18:31:30 -0700253 for (std::map<int, shared_ptr<SubprocessRecord>>::iterator it =
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800254 subprocess_records_.begin();
255 it != subprocess_records_.end(); ++it) {
256 if (it->second->callback)
257 return true;
258 }
259 return false;
260}
261
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700262Subprocess* Subprocess::subprocess_singleton_ = nullptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000263
264} // namespace chromeos_update_engine