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