blob: d517797e247014ad0144edcb29e086cd4f2c743f [file] [log] [blame]
Felipe Lemef0292972016-11-22 13:57:05 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "dumpstate"
18
19#include "DumpstateUtil.h"
20
Ecco Park61ffcf72016-10-27 15:46:26 -070021#include <dirent.h>
Felipe Lemef0292972016-11-22 13:57:05 -080022#include <fcntl.h>
23#include <sys/prctl.h>
24#include <sys/wait.h>
25#include <unistd.h>
26
27#include <vector>
28
Ecco Park61ffcf72016-10-27 15:46:26 -070029#include <android-base/file.h>
Felipe Lemef0292972016-11-22 13:57:05 -080030#include <android-base/properties.h>
Ecco Park61ffcf72016-10-27 15:46:26 -070031#include <android-base/stringprintf.h>
32#include <android-base/strings.h>
Felipe Lemef0292972016-11-22 13:57:05 -080033#include <cutils/log.h>
34
35#include "DumpstateInternal.h"
36
37// TODO: move to unnamed namespace
38static constexpr const char* kSuPath = "/system/xbin/su";
39
40// TODO: move to unnamed namespace
41static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
42 sigset_t child_mask, old_mask;
43 sigemptyset(&child_mask);
44 sigaddset(&child_mask, SIGCHLD);
45
46 if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
47 printf("*** sigprocmask failed: %s\n", strerror(errno));
48 return false;
49 }
50
51 timespec ts;
52 ts.tv_sec = timeout_seconds;
53 ts.tv_nsec = 0;
54 int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts));
55 int saved_errno = errno;
56 // Set the signals back the way they were.
57 if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
58 printf("*** sigprocmask failed: %s\n", strerror(errno));
59 if (ret == 0) {
60 return false;
61 }
62 }
63 if (ret == -1) {
64 errno = saved_errno;
65 if (errno == EAGAIN) {
66 errno = ETIMEDOUT;
67 } else {
68 printf("*** sigtimedwait failed: %s\n", strerror(errno));
69 }
70 return false;
71 }
72
73 pid_t child_pid = waitpid(pid, status, WNOHANG);
74 if (child_pid != pid) {
75 if (child_pid != -1) {
76 printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
77 } else {
78 printf("*** waitpid failed: %s\n", strerror(errno));
79 }
80 return false;
81 }
82 return true;
83}
84
85CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
86CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build();
87CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
88
89CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
90}
91
92CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
93 values.always_ = true;
94 return *this;
95}
96
97CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
98 values.account_mode_ = SU_ROOT;
99 return *this;
100}
101
102CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
103 values.account_mode_ = DROP_ROOT;
104 return *this;
105}
106
107CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
108 values.output_mode_ = REDIRECT_TO_STDERR;
109 return *this;
110}
111
112CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
113 const std::string& message) {
114 values.logging_message_ = message;
115 return *this;
116}
117
118CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
119 return CommandOptions(values);
120}
121
122CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
123 : timeout_(timeout),
124 always_(false),
125 account_mode_(DONT_DROP_ROOT),
126 output_mode_(NORMAL_OUTPUT),
127 logging_message_("") {
128}
129
130CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
131}
132
133int64_t CommandOptions::Timeout() const {
134 return values.timeout_;
135}
136
137bool CommandOptions::Always() const {
138 return values.always_;
139}
140
141PrivilegeMode CommandOptions::PrivilegeMode() const {
142 return values.account_mode_;
143}
144
145OutputMode CommandOptions::OutputMode() const {
146 return values.output_mode_;
147}
148
149std::string CommandOptions::LoggingMessage() const {
150 return values.logging_message_;
151}
152
153CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
154 return CommandOptions::CommandOptionsBuilder(timeout);
155}
156
157std::string PropertiesHelper::build_type_ = "";
158int PropertiesHelper::dry_run_ = -1;
159
160bool PropertiesHelper::IsUserBuild() {
161 if (build_type_.empty()) {
162 build_type_ = android::base::GetProperty("ro.build.type", "user");
163 }
164 return "user" == build_type_;
165}
166
167bool PropertiesHelper::IsDryRun() {
168 if (dry_run_ == -1) {
169 dry_run_ = android::base::GetBoolProperty("dumpstate.dry_run", false) ? 1 : 0;
170 }
171 return dry_run_ == 1;
172}
173
174int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) {
175 int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
176 if (fd < 0) {
177 int err = errno;
178 if (title.empty()) {
179 dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err));
180 } else {
181 dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(),
182 strerror(err));
183 }
184 fsync(out_fd);
185 return -1;
186 }
187 return DumpFileFromFdToFd(title, path, fd, out_fd, PropertiesHelper::IsDryRun());
188}
189
190int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command,
191 const CommandOptions& options) {
192 if (full_command.empty()) {
193 MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str());
194 return -1;
195 }
196
197 int size = full_command.size() + 1; // null terminated
198 int starting_index = 0;
199 if (options.PrivilegeMode() == SU_ROOT) {
200 starting_index = 2; // "su" "root"
201 size += starting_index;
202 }
203
204 std::vector<const char*> args;
205 args.resize(size);
206
207 std::string command_string;
208 if (options.PrivilegeMode() == SU_ROOT) {
209 args[0] = kSuPath;
210 command_string += kSuPath;
211 args[1] = "root";
212 command_string += " root ";
213 }
214 for (size_t i = 0; i < full_command.size(); i++) {
215 args[i + starting_index] = full_command[i].data();
216 command_string += args[i + starting_index];
217 if (i != full_command.size() - 1) {
218 command_string += " ";
219 }
220 }
221 args[size - 1] = nullptr;
222
223 const char* command = command_string.c_str();
224
225 if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) {
226 dprintf(fd, "Skipping '%s' on user build.\n", command);
227 return 0;
228 }
229
230 if (!title.empty()) {
231 dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command);
232 fsync(fd);
233 }
234
235 const std::string& logging_message = options.LoggingMessage();
236 if (!logging_message.empty()) {
237 MYLOGI(logging_message.c_str(), command_string.c_str());
238 }
239
240 bool silent = (options.OutputMode() == REDIRECT_TO_STDERR);
241 bool redirecting_to_fd = STDOUT_FILENO != fd;
242
243 if (PropertiesHelper::IsDryRun() && !options.Always()) {
244 if (!title.empty()) {
245 dprintf(fd, "\t(skipped on dry run)\n");
246 } else if (redirecting_to_fd) {
247 // There is no title, but we should still print a dry-run message
248 dprintf(fd, "%s: skipped on dry run\n", command_string.c_str());
249 }
250 fsync(fd);
251 return 0;
252 }
253
254 const char* path = args[0];
255
256 uint64_t start = Nanotime();
257 pid_t pid = fork();
258
259 /* handle error case */
260 if (pid < 0) {
261 if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno));
262 MYLOGE("*** fork: %s\n", strerror(errno));
263 return pid;
264 }
265
266 /* handle child case */
267 if (pid == 0) {
268 if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) {
269 if (!silent) {
270 dprintf(fd, "*** failed to drop root before running %s: %s\n", command,
271 strerror(errno));
272 }
273 MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
274 return -1;
275 }
276
277 if (silent) {
278 // Redirects stdout to stderr
279 TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO));
280 } else if (redirecting_to_fd) {
281 // Redirect stdout to fd
282 TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
283 close(fd);
284 }
285
286 /* make sure the child dies when dumpstate dies */
287 prctl(PR_SET_PDEATHSIG, SIGKILL);
288
289 /* just ignore SIGPIPE, will go down with parent's */
290 struct sigaction sigact;
291 memset(&sigact, 0, sizeof(sigact));
292 sigact.sa_handler = SIG_IGN;
293 sigaction(SIGPIPE, &sigact, NULL);
294
295 execvp(path, (char**)args.data());
296 // execvp's result will be handled after waitpid_with_timeout() below, but
297 // if it failed, it's safer to exit dumpstate.
298 MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
299 // Must call _exit (instead of exit), otherwise it will corrupt the zip
300 // file.
301 _exit(EXIT_FAILURE);
302 }
303
304 /* handle parent case */
305 int status;
306 bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
307 fsync(fd);
308
309 uint64_t elapsed = Nanotime() - start;
310 if (!ret) {
311 if (errno == ETIMEDOUT) {
312 if (!silent)
313 dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
314 static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
315 MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
316 static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
317 } else {
318 if (!silent)
319 dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command,
320 static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
321 MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command,
322 static_cast<float>(elapsed) / NANOS_PER_SEC, pid);
323 }
324 kill(pid, SIGTERM);
325 if (!waitpid_with_timeout(pid, 5, nullptr)) {
326 kill(pid, SIGKILL);
327 if (!waitpid_with_timeout(pid, 5, nullptr)) {
328 if (!silent)
329 dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n",
330 command, pid);
331 MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
332 }
333 }
334 return -1;
335 }
336
337 if (WIFSIGNALED(status)) {
338 if (!silent)
339 dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
340 MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
341 } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
342 status = WEXITSTATUS(status);
343 if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status);
344 MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
345 }
346
347 return status;
348}
Ecco Park61ffcf72016-10-27 15:46:26 -0700349
350int GetPidByName(const std::string& ps_name) {
351 DIR* proc_dir;
352 struct dirent* ps;
353 unsigned int pid;
354 std::string cmdline;
355
356 if (!(proc_dir = opendir("/proc"))) {
357 MYLOGE("Can't open /proc\n");
358 return -1;
359 }
360
361 while ((ps = readdir(proc_dir))) {
362 if (!(pid = atoi(ps->d_name))) {
363 continue;
364 }
365 android::base::ReadFileToString("/proc/" + std::string(ps->d_name) + "/cmdline", &cmdline);
366 if (cmdline.find(ps_name) == std::string::npos) {
367 continue;
368 } else {
369 closedir(proc_dir);
370 return pid;
371 }
372 }
373 MYLOGE("can't find the pid\n");
374 closedir(proc_dir);
375 return -1;
376}