OLD | NEW |
1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "update_engine/subprocess.h" | 5 #include "update_engine/subprocess.h" |
6 #include <stdlib.h> | 6 #include <stdlib.h> |
7 #include <string.h> | 7 #include <string.h> |
8 #include <string> | 8 #include <string> |
9 #include <unistd.h> | 9 #include <unistd.h> |
10 #include <vector> | 10 #include <vector> |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/scoped_ptr.h" | 12 #include "base/scoped_ptr.h" |
13 #include "base/string_util.h" | 13 #include "base/string_util.h" |
14 | 14 |
15 using std::string; | 15 using std::string; |
| 16 using std::tr1::shared_ptr; |
16 using std::vector; | 17 using std::vector; |
17 | 18 |
18 namespace chromeos_update_engine { | 19 namespace chromeos_update_engine { |
19 | 20 |
20 void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) { | 21 void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) { |
21 COMPILE_ASSERT(sizeof(guint) == sizeof(uint32_t), | 22 SubprocessRecord* record = reinterpret_cast<SubprocessRecord*>(data); |
22 guint_uint32_size_mismatch); | 23 |
23 guint* tag = reinterpret_cast<guint*>(data); | 24 // Make sure we read any remaining process output. Then close the pipe. |
24 const SubprocessCallbackRecord& record = Get().callback_records_[*tag]; | 25 GStdoutWatchCallback(record->gioout, G_IO_IN, &record->stdout); |
25 if (record.callback) | 26 int fd = g_io_channel_unix_get_fd(record->gioout); |
26 record.callback(status, record.callback_data); | 27 g_source_remove(record->gioout_tag); |
| 28 g_io_channel_unref(record->gioout); |
| 29 close(fd); |
| 30 |
27 g_spawn_close_pid(pid); | 31 g_spawn_close_pid(pid); |
28 Get().callback_records_.erase(*tag); | 32 if (status) { |
29 delete tag; | 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); |
30 } | 42 } |
31 | 43 |
32 void Subprocess::GRedirectStderrToStdout(gpointer user_data) { | 44 void Subprocess::GRedirectStderrToStdout(gpointer user_data) { |
33 dup2(1, 2); | 45 dup2(1, 2); |
34 } | 46 } |
35 | 47 |
| 48 gboolean 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 |
36 namespace { | 65 namespace { |
37 void FreeArgv(char** argv) { | 66 void FreeArgv(char** argv) { |
38 for (int i = 0; argv[i]; i++) { | 67 for (int i = 0; argv[i]; i++) { |
39 free(argv[i]); | 68 free(argv[i]); |
40 argv[i] = NULL; | 69 argv[i] = NULL; |
41 } | 70 } |
42 } | 71 } |
43 | 72 |
44 void FreeArgvInError(char** argv) { | 73 void FreeArgvInError(char** argv) { |
45 FreeArgv(argv); | 74 FreeArgv(argv); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 } | 128 } |
100 argv[cmd.size()] = NULL; | 129 argv[cmd.size()] = NULL; |
101 | 130 |
102 char** argp = ArgPointer(); | 131 char** argp = ArgPointer(); |
103 if (!argp) { | 132 if (!argp) { |
104 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv. | 133 FreeArgvInError(argv.get()); // NULL in argv[i] terminates argv. |
105 return 0; | 134 return 0; |
106 } | 135 } |
107 ScopedFreeArgPointer argp_free(argp); | 136 ScopedFreeArgPointer argp_free(argp); |
108 | 137 |
109 SubprocessCallbackRecord callback_record; | 138 shared_ptr<SubprocessRecord> record(new SubprocessRecord); |
110 callback_record.callback = callback; | 139 record->callback = callback; |
111 callback_record.callback_data = p; | 140 record->callback_data = p; |
112 | 141 gint stdout_fd = -1; |
113 bool success = g_spawn_async(NULL, // working directory | 142 bool success = g_spawn_async_with_pipes( |
114 argv.get(), | 143 NULL, // working directory |
115 argp, | 144 argv.get(), |
116 G_SPAWN_DO_NOT_REAP_CHILD, // flags | 145 argp, |
117 NULL, // child setup function | 146 G_SPAWN_DO_NOT_REAP_CHILD, // flags |
118 NULL, // child setup data pointer | 147 GRedirectStderrToStdout, // child setup function |
119 &child_pid, | 148 NULL, // child setup data pointer |
120 &err); | 149 &child_pid, |
| 150 NULL, |
| 151 &stdout_fd, |
| 152 NULL, |
| 153 &err); |
121 FreeArgv(argv.get()); | 154 FreeArgv(argv.get()); |
122 if (!success) { | 155 if (!success) { |
123 LOG(ERROR) << "g_spawn_async failed"; | 156 LOG(ERROR) << "g_spawn_async failed"; |
124 return 0; | 157 return 0; |
125 } | 158 } |
126 guint* tag = new guint; | 159 record->tag = |
127 *tag = g_child_watch_add(child_pid, GChildExitedCallback, tag); | 160 g_child_watch_add(child_pid, GChildExitedCallback, record.get()); |
128 callback_records_[*tag] = callback_record; | 161 subprocess_records_[record->tag] = record; |
129 return *tag; | 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; |
130 } | 175 } |
131 | 176 |
132 void Subprocess::CancelExec(uint32_t tag) { | 177 void Subprocess::CancelExec(uint32_t tag) { |
133 if (callback_records_[tag].callback) { | 178 subprocess_records_[tag]->callback = NULL; |
134 callback_records_[tag].callback = NULL; | |
135 } | |
136 } | 179 } |
137 | 180 |
138 bool Subprocess::SynchronousExecFlags(const std::vector<std::string>& cmd, | 181 bool Subprocess::SynchronousExecFlags(const std::vector<std::string>& cmd, |
139 int* return_code, | 182 int* return_code, |
140 GSpawnFlags flags) { | 183 GSpawnFlags flags) { |
141 GError* err = NULL; | 184 GError* err = NULL; |
142 scoped_array<char*> argv(new char*[cmd.size() + 1]); | 185 scoped_array<char*> argv(new char*[cmd.size() + 1]); |
143 for (unsigned int i = 0; i < cmd.size(); i++) { | 186 for (unsigned int i = 0; i < cmd.size(); i++) { |
144 argv[i] = strdup(cmd[i].c_str()); | 187 argv[i] = strdup(cmd[i].c_str()); |
145 if (!argv[i]) { | 188 if (!argv[i]) { |
(...skipping 25 matching lines...) Expand all Loading... |
171 return_code, | 214 return_code, |
172 &err); | 215 &err); |
173 FreeArgv(argv.get()); | 216 FreeArgv(argv.get()); |
174 if (err) | 217 if (err) |
175 LOG(INFO) << "err is: " << err->code << ", " << err->message; | 218 LOG(INFO) << "err is: " << err->code << ", " << err->message; |
176 if (child_stdout && strlen(child_stdout)) | 219 if (child_stdout && strlen(child_stdout)) |
177 LOG(INFO) << "Subprocess output:\n" << child_stdout; | 220 LOG(INFO) << "Subprocess output:\n" << child_stdout; |
178 return success; | 221 return success; |
179 } | 222 } |
180 | 223 |
| 224 bool 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 |
181 Subprocess* Subprocess::subprocess_singleton_ = NULL; | 234 Subprocess* Subprocess::subprocess_singleton_ = NULL; |
182 | 235 |
183 } // namespace chromeos_update_engine | 236 } // namespace chromeos_update_engine |
OLD | NEW |