blob: 8671584d637de3e177d9d59560d978c6f0df0e39 [file] [log] [blame]
adlr@google.com3defe6a2009-12-04 20:57:17 +00001// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// 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
44// Note: Caller responsible for free()ing the returned value!
45char** ArgPointer() {
46 const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
47 char** ret = new char*[arraysize(keys) + 1];
48 int pointer = 0;
49 for (size_t i = 0; i < arraysize(keys); i++) {
50 ret[i] = NULL;
51 if (getenv(keys[i])) {
52 ret[pointer] = strdup(StringPrintf("%s=%s", keys[i],
53 getenv(keys[i])).c_str());
54 pointer++;
55 }
56 }
57 return ret;
58}
59
60class ScopedFreeArgPointer {
61 public:
62 ScopedFreeArgPointer(char** arr) : arr_(arr) {}
63 ~ScopedFreeArgPointer() {
64 if (!arr_)
65 return;
66 for (int i = 0; arr_[i]; i++)
67 free(arr_[i]);
68 delete[] arr_;
69 }
70 private:
71 char** arr_;
72 DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
73};
adlr@google.com3defe6a2009-12-04 20:57:17 +000074} // namespace {}
75
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070076uint32_t Subprocess::Exec(const std::vector<std::string>& cmd,
Andrew de los Reyes3270f742010-07-15 22:28:14 -070077 ExecCallback callback,
78 void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +000079 GPid child_pid;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070080 GError* err;
81 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +000082 for (unsigned int i = 0; i < cmd.size(); i++) {
83 argv[i] = strdup(cmd[i].c_str());
84 }
85 argv[cmd.size()] = NULL;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070086
Andrew de los Reyes3270f742010-07-15 22:28:14 -070087 char** argp = ArgPointer();
88 ScopedFreeArgPointer argp_free(argp);
adlr@google.com3defe6a2009-12-04 20:57:17 +000089
90 SubprocessCallbackRecord callback_record;
91 callback_record.callback = callback;
92 callback_record.callback_data = p;
93
94 bool success = g_spawn_async(NULL, // working directory
95 argv.get(),
Andrew de los Reyes3270f742010-07-15 22:28:14 -070096 argp,
adlr@google.com3defe6a2009-12-04 20:57:17 +000097 G_SPAWN_DO_NOT_REAP_CHILD, // flags
98 NULL, // child setup function
99 NULL, // child setup data pointer
100 &child_pid,
101 &err);
102 FreeArgv(argv.get());
103 if (!success) {
104 LOG(ERROR) << "g_spawn_async failed";
105 return 0;
106 }
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700107 guint* tag = new guint;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000108 *tag = g_child_watch_add(child_pid, GChildExitedCallback, tag);
109 callback_records_[*tag] = callback_record;
110 return *tag;
111}
112
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700113void Subprocess::CancelExec(uint32_t tag) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000114 if (callback_records_[tag].callback) {
115 callback_records_[tag].callback = NULL;
116 }
117}
118
119bool Subprocess::SynchronousExec(const std::vector<std::string>& cmd,
120 int* return_code) {
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700121 GError* err = NULL;
122 scoped_array<char*> argv(new char*[cmd.size() + 1]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000123 for (unsigned int i = 0; i < cmd.size(); i++) {
124 argv[i] = strdup(cmd[i].c_str());
125 }
126 argv[cmd.size()] = NULL;
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700127
128 char** argp = ArgPointer();
129 ScopedFreeArgPointer argp_free(argp);
130
131 char* child_stdout;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000132
133 bool success = g_spawn_sync(NULL, // working directory
134 argv.get(),
135 argp,
Kenneth Watersa7fcafa2010-09-21 10:27:03 -0700136 G_SPAWN_STDERR_TO_DEV_NULL, // flags
137 GRedirectStderrToStdout, // child setup function
adlr@google.com3defe6a2009-12-04 20:57:17 +0000138 NULL, // data for child setup function
Andrew de los Reyes3270f742010-07-15 22:28:14 -0700139 &child_stdout,
Kenneth Watersa7fcafa2010-09-21 10:27:03 -0700140 NULL,
adlr@google.com3defe6a2009-12-04 20:57:17 +0000141 return_code,
142 &err);
143 FreeArgv(argv.get());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700144 if (err)
145 LOG(INFO) << "err is: " << err->code << ", " << err->message;
Andrew de los Reyesbef0c7d2010-08-20 10:20:10 -0700146 if (child_stdout && strlen(child_stdout))
Kenneth Watersa7fcafa2010-09-21 10:27:03 -0700147 LOG(INFO) << "Subprocess output:\n" << child_stdout;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000148 return success;
149}
150
151Subprocess* Subprocess::subprocess_singleton_ = NULL;
152
153} // namespace chromeos_update_engine