blob: 28b895c1211b730db8b30e95686efa56e6b4f1c3 [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;
16using std::vector;
17
18namespace chromeos_update_engine {
19
20void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070021 COMPILE_ASSERT(sizeof(guint) == sizeof(uint32_t),
adlr@google.com3defe6a2009-12-04 20:57:17 +000022 guint_uint32_size_mismatch);
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070023 guint* tag = reinterpret_cast<guint*>(data);
adlr@google.com3defe6a2009-12-04 20:57:17 +000024 const SubprocessCallbackRecord& record = Get().callback_records_[*tag];
25 if (record.callback)
26 record.callback(status, record.callback_data);
27 g_spawn_close_pid(pid);
28 Get().callback_records_.erase(*tag);
29 delete tag;
30}
31
Kenneth Watersa7fcafa2010-09-21 10:27:03 -070032void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
33 dup2(1, 2);
34}
35
adlr@google.com3defe6a2009-12-04 20:57:17 +000036namespace {
37void FreeArgv(char** argv) {
38 for (int i = 0; argv[i]; i++) {
39 free(argv[i]);
40 argv[i] = NULL;
41 }
42}
Andrew de los Reyes3270f742010-07-15 22:28:14 -070043
Chris Masonec6c57a52010-09-23 13:06:14 -070044void FreeArgvInError(char** argv) {
45 FreeArgv(argv);
46 LOG(ERROR) << "Ran out of memory copying args.";
47}
48
Andrew de los Reyes3270f742010-07-15 22:28:14 -070049// Note: Caller responsible for free()ing the returned value!
Chris Masonec6c57a52010-09-23 13:06:14 -070050// Will return NULL on failure and free any allocated memory.
Andrew de los Reyes3270f742010-07-15 22:28:14 -070051char** ArgPointer() {
52 const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
53 char** ret = new char*[arraysize(keys) + 1];
54 int pointer = 0;
55 for (size_t i = 0; i < arraysize(keys); i++) {
Andrew de los Reyes3270f742010-07-15 22:28:14 -070056 if (getenv(keys[i])) {
57 ret[pointer] = strdup(StringPrintf("%s=%s", keys[i],
58 getenv(keys[i])).c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -070059 if (!ret[pointer]) {
60 FreeArgv(ret);
61 delete [] ret;
62 return NULL;
63 }
64 ++pointer;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070065 }
66 }
Chris Masonec6c57a52010-09-23 13:06:14 -070067 ret[pointer] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070068 return ret;
69}
70
71class ScopedFreeArgPointer {
72 public:
73 ScopedFreeArgPointer(char** arr) : arr_(arr) {}
74 ~ScopedFreeArgPointer() {
75 if (!arr_)
76 return;
77 for (int i = 0; arr_[i]; i++)
78 free(arr_[i]);
79 delete[] arr_;
80 }
81 private:
82 char** arr_;
83 DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
84};
adlr@google.com3defe6a2009-12-04 20:57:17 +000085} // namespace {}
86
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070087uint32_t Subprocess::Exec(const std::vector<std::string>& cmd,
Andrew de los Reyes3270f742010-07-15 22:28:14 -070088 ExecCallback callback,
89 void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +000090 GPid child_pid;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070091 GError* err;
92 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +000093 for (unsigned int i = 0; i < cmd.size(); i++) {
94 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -070095 if (!argv[i]) {
96 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
97 return 0;
98 }
adlr@google.com3defe6a2009-12-04 20:57:17 +000099 }
100 argv[cmd.size()] = NULL;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700101
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700102 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700103 if (!argp) {
104 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
105 return 0;
106 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700107 ScopedFreeArgPointer argp_free(argp);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000108
109 SubprocessCallbackRecord callback_record;
110 callback_record.callback = callback;
111 callback_record.callback_data = p;
112
113 bool success = g_spawn_async(NULL, // working directory
114 argv.get(),
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700115 argp,
adlr@google.com3defe6a2009-12-04 20:57:17 +0000116 G_SPAWN_DO_NOT_REAP_CHILD, // flags
117 NULL, // child setup function
118 NULL, // child setup data pointer
119 &child_pid,
120 &err);
121 FreeArgv(argv.get());
122 if (!success) {
123 LOG(ERROR) << "g_spawn_async failed";
124 return 0;
125 }
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700126 guint* tag = new guint;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000127 *tag = g_child_watch_add(child_pid, GChildExitedCallback, tag);
128 callback_records_[*tag] = callback_record;
129 return *tag;
130}
131
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700132void Subprocess::CancelExec(uint32_t tag) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000133 if (callback_records_[tag].callback) {
134 callback_records_[tag].callback = NULL;
135 }
136}
137
138bool Subprocess::SynchronousExec(const std::vector<std::string>& cmd,
139 int* return_code) {
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700140 GError* err = NULL;
141 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000142 for (unsigned int i = 0; i < cmd.size(); i++) {
143 argv[i] = strdup(cmd[i].c_str());
Chris Masonec6c57a52010-09-23 13:06:14 -0700144 if (!argv[i]) {
145 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
146 return false;
147 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000148 }
149 argv[cmd.size()] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700150
151 char** argp = ArgPointer();
Chris Masonec6c57a52010-09-23 13:06:14 -0700152 if (!argp) {
153 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv.
154 return false;
155 }
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700156 ScopedFreeArgPointer argp_free(argp);
157
158 char* child_stdout;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000159
160 bool success = g_spawn_sync(NULL, // working directory
161 argv.get(),
162 argp,
Kenneth Watersa7fcafa2010-09-21 10:27:03 -0700163 G_SPAWN_STDERR_TO_DEV_NULL, // flags
164 GRedirectStderrToStdout, // child setup function
adlr@google.com3defe6a2009-12-04 20:57:17 +0000165 NULL, // data for child setup function
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700166 &child_stdout,
Kenneth Watersa7fcafa2010-09-21 10:27:03 -0700167 NULL,
adlr@google.com3defe6a2009-12-04 20:57:17 +0000168 return_code,
169 &err);
170 FreeArgv(argv.get());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700171 if (err)
172 LOG(INFO) << "err is: " << err->code << ", " << err->message;
Andrew de los Reyesbef0c7d2010-08-20 10:20:10 -0700173 if (child_stdout && strlen(child_stdout))
Kenneth Watersa7fcafa2010-09-21 10:27:03 -0700174 LOG(INFO) << "Subprocess output:\n" << child_stdout;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000175 return success;
176}
177
178Subprocess* Subprocess::subprocess_singleton_ = NULL;
179
180} // namespace chromeos_update_engine