blob: e2ab793a97c1410b1d67b714437a87f4bbcfffc8 [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
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>
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;
23using std::vector;
24
25namespace chromeos_update_engine {
26
27void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080028 SubprocessRecord* record = reinterpret_cast<SubprocessRecord*>(data);
29
30 // Make sure we read any remaining process output. Then close the pipe.
31 GStdoutWatchCallback(record->gioout, G_IO_IN, &record->stdout);
32 int fd = g_io_channel_unix_get_fd(record->gioout);
33 g_source_remove(record->gioout_tag);
34 g_io_channel_unref(record->gioout);
35 close(fd);
36
adlr@google.com3defe6a2009-12-04 20:57:17 +000037 g_spawn_close_pid(pid);
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070038 gint use_status = status;
39 if (WIFEXITED(status))
40 use_status = WEXITSTATUS(status);
41
Darin Petkov6f03a3b2010-11-10 14:27:14 -080042 if (status) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070043 LOG(INFO) << "Subprocess status: " << use_status;
Darin Petkov6f03a3b2010-11-10 14:27:14 -080044 }
45 if (!record->stdout.empty()) {
46 LOG(INFO) << "Subprocess output:\n" << record->stdout;
47 }
48 if (record->callback) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070049 record->callback(use_status, record->stdout, record->callback_data);
Darin Petkov6f03a3b2010-11-10 14:27:14 -080050 }
51 Get().subprocess_records_.erase(record->tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +000052}
53
Kenneth Watersa7fcafa2010-09-21 10:27:03 -070054void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
55 dup2(1, 2);
56}
57
Darin Petkov6f03a3b2010-11-10 14:27:14 -080058gboolean Subprocess::GStdoutWatchCallback(GIOChannel* source,
59 GIOCondition condition,
60 gpointer data) {
61 string* stdout = reinterpret_cast<string*>(data);
62 char buf[1024];
63 gsize bytes_read;
64 while (g_io_channel_read_chars(source,
65 buf,
66 arraysize(buf),
67 &bytes_read,
Alex Vakulenko88b591f2014-08-28 16:48:57 -070068 nullptr) == G_IO_STATUS_NORMAL &&
Darin Petkov6f03a3b2010-11-10 14:27:14 -080069 bytes_read > 0) {
70 stdout->append(buf, bytes_read);
71 }
72 return TRUE; // Keep the callback source. It's freed in GChilExitedCallback.
73}
74
adlr@google.com3defe6a2009-12-04 20:57:17 +000075namespace {
76void FreeArgv(char** argv) {
77 for (int i = 0; argv[i]; i++) {
78 free(argv[i]);
Alex Vakulenko88b591f2014-08-28 16:48:57 -070079 argv[i] = nullptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +000080 }
81}
Andrew de los Reyes3270f742010-07-15 22:28:14 -070082
Chris Masonec6c57a52010-09-23 13:06:14 -070083void FreeArgvInError(char** argv) {
84 FreeArgv(argv);
85 LOG(ERROR) << "Ran out of memory copying args.";
86}
87
Andrew de los Reyes3270f742010-07-15 22:28:14 -070088// Note: Caller responsible for free()ing the returned value!
Alex Vakulenko88b591f2014-08-28 16:48:57 -070089// Will return null on failure and free any allocated memory.
Andrew de los Reyes3270f742010-07-15 22:28:14 -070090char** ArgPointer() {
91 const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
92 char** ret = new char*[arraysize(keys) + 1];
93 int pointer = 0;
94 for (size_t i = 0; i < arraysize(keys); i++) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070095 if (getenv(keys[i])) {
Alex Vakulenko75039d72014-03-25 12:36:28 -070096 ret[pointer] = strdup(base::StringPrintf("%s=%s", keys[i],
97 getenv(keys[i])).c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -070098 if (!ret[pointer]) {
99 FreeArgv(ret);
100 delete [] ret;
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700101 return nullptr;
Chris Masonec6c57a52010-09-23 13:06:14 -0700102 }
103 ++pointer;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700104 }
105 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700106 ret[pointer] = nullptr;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700107 return ret;
108}
109
110class ScopedFreeArgPointer {
111 public:
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700112 explicit ScopedFreeArgPointer(char** arr) : arr_(arr) {}
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700113 ~ScopedFreeArgPointer() {
114 if (!arr_)
115 return;
116 for (int i = 0; arr_[i]; i++)
117 free(arr_[i]);
118 delete[] arr_;
119 }
120 private:
121 char** arr_;
122 DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
123};
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700124} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +0000125
Darin Petkov85d02b72011-05-17 13:25:51 -0700126uint32_t Subprocess::Exec(const vector<string>& cmd,
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700127 ExecCallback callback,
128 void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000129 GPid child_pid;
Ben Chan8303a862014-01-27 23:24:02 -0800130 scoped_ptr<char*[]> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000131 for (unsigned int i = 0; i < cmd.size(); i++) {
132 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700133 if (!argv[i]) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700134 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700135 return 0;
136 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000137 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700138 argv[cmd.size()] = nullptr;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700139
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700140 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700141 if (!argp) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700142 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700143 return 0;
144 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700145 ScopedFreeArgPointer argp_free(argp);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000146
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800147 shared_ptr<SubprocessRecord> record(new SubprocessRecord);
148 record->callback = callback;
149 record->callback_data = p;
150 gint stdout_fd = -1;
Alex Deymo3e0b53e2014-08-12 23:12:25 -0700151 GError* error = nullptr;
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800152 bool success = g_spawn_async_with_pipes(
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700153 nullptr, // working directory
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800154 argv.get(),
155 argp,
156 G_SPAWN_DO_NOT_REAP_CHILD, // flags
157 GRedirectStderrToStdout, // child setup function
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700158 nullptr, // child setup data pointer
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800159 &child_pid,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700160 nullptr,
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800161 &stdout_fd,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700162 nullptr,
Alex Deymo3e0b53e2014-08-12 23:12:25 -0700163 &error);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000164 FreeArgv(argv.get());
165 if (!success) {
Alex Deymo3e0b53e2014-08-12 23:12:25 -0700166 LOG(ERROR) << "g_spawn_async failed: " << utils::GetAndFreeGError(&error);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000167 return 0;
168 }
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800169 record->tag =
170 g_child_watch_add(child_pid, GChildExitedCallback, record.get());
171 subprocess_records_[record->tag] = record;
172
173 // Capture the subprocess output.
174 record->gioout = g_io_channel_unix_new(stdout_fd);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700175 g_io_channel_set_encoding(record->gioout, nullptr, nullptr);
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800176 LOG_IF(WARNING,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700177 g_io_channel_set_flags(record->gioout, G_IO_FLAG_NONBLOCK, nullptr) !=
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800178 G_IO_STATUS_NORMAL) << "Unable to set non-blocking I/O mode.";
179 record->gioout_tag = g_io_add_watch(
180 record->gioout,
181 static_cast<GIOCondition>(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
182 GStdoutWatchCallback,
183 &record->stdout);
184 return record->tag;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000185}
186
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700187void Subprocess::CancelExec(uint32_t tag) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700188 subprocess_records_[tag]->callback = nullptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000189}
190
Darin Petkov85d02b72011-05-17 13:25:51 -0700191bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
192 GSpawnFlags flags,
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700193 int* return_code,
Darin Petkov85d02b72011-05-17 13:25:51 -0700194 string* stdout) {
195 if (stdout) {
196 *stdout = "";
197 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700198 GError* err = nullptr;
Ben Chan8303a862014-01-27 23:24:02 -0800199 scoped_ptr<char*[]> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000200 for (unsigned int i = 0; i < cmd.size(); i++) {
201 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700202 if (!argv[i]) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700203 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700204 return false;
205 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000206 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700207 argv[cmd.size()] = nullptr;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700208
209 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700210 if (!argp) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700211 FreeArgvInError(argv.get()); // null in argv[i] terminates argv.
Chris Masonec6c57a52010-09-23 13:06:14 -0700212 return false;
213 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700214 ScopedFreeArgPointer argp_free(argp);
215
216 char* child_stdout;
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700217 bool success = g_spawn_sync(
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700218 nullptr, // working directory
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700219 argv.get(),
220 argp,
Andrew de los Reyes50f36492010-11-01 13:57:12 -0700221 static_cast<GSpawnFlags>(G_SPAWN_STDERR_TO_DEV_NULL |
222 G_SPAWN_SEARCH_PATH | flags), // flags
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700223 GRedirectStderrToStdout, // child setup function
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700224 nullptr, // data for child setup function
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700225 &child_stdout,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700226 nullptr,
Andrew de los Reyes5a232832010-10-12 16:20:54 -0700227 return_code,
228 &err);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000229 FreeArgv(argv.get());
Darin Petkova0b9e772011-10-06 05:05:56 -0700230 LOG_IF(INFO, err) << utils::GetAndFreeGError(&err);
Darin Petkov478435e2011-05-17 11:46:31 -0700231 if (child_stdout) {
Darin Petkov85d02b72011-05-17 13:25:51 -0700232 if (stdout) {
233 *stdout = child_stdout;
234 } else if (*child_stdout) {
Darin Petkov478435e2011-05-17 11:46:31 -0700235 LOG(INFO) << "Subprocess output:\n" << child_stdout;
236 }
237 g_free(child_stdout);
238 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000239 return success;
240}
241
Darin Petkov85d02b72011-05-17 13:25:51 -0700242bool Subprocess::SynchronousExec(const std::vector<std::string>& cmd,
243 int* return_code,
244 std::string* stdout) {
245 return SynchronousExecFlags(cmd,
246 static_cast<GSpawnFlags>(0),
247 return_code,
248 stdout);
249}
250
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800251bool Subprocess::SubprocessInFlight() {
252 for (std::map<int, shared_ptr<SubprocessRecord> >::iterator it =
253 subprocess_records_.begin();
254 it != subprocess_records_.end(); ++it) {
255 if (it->second->callback)
256 return true;
257 }
258 return false;
259}
260
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700261Subprocess* Subprocess::subprocess_singleton_ = nullptr;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000262
263} // namespace chromeos_update_engine