1 /* 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 21 #include <dirent.h> 22 #include <fcntl.h> 23 #include <sys/prctl.h> 24 #include <sys/wait.h> 25 #include <unistd.h> 26 27 #include <vector> 28 29 #include <android-base/file.h> 30 #include <android-base/properties.h> 31 #include <android-base/stringprintf.h> 32 #include <android-base/strings.h> 33 #include <android-base/unique_fd.h> 34 #include <log/log.h> 35 36 #include "DumpstateInternal.h" 37 38 namespace android { 39 namespace os { 40 namespace dumpstate { 41 42 namespace { 43 44 static constexpr const char* kSuPath = "/system/xbin/su"; 45 waitpid_with_timeout(pid_t pid,int timeout_ms,int * status)46 static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) { 47 sigset_t child_mask, old_mask; 48 sigemptyset(&child_mask); 49 sigaddset(&child_mask, SIGCHLD); 50 51 // block SIGCHLD before we check if a process has exited 52 if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) { 53 printf("*** sigprocmask failed: %s\n", strerror(errno)); 54 return false; 55 } 56 57 // if the child has exited already, handle and reset signals before leaving 58 pid_t child_pid = waitpid(pid, status, WNOHANG); 59 if (child_pid != pid) { 60 if (child_pid > 0) { 61 printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid); 62 sigprocmask(SIG_SETMASK, &old_mask, nullptr); 63 return false; 64 } 65 } else { 66 sigprocmask(SIG_SETMASK, &old_mask, nullptr); 67 return true; 68 } 69 70 // wait for a SIGCHLD 71 timespec ts; 72 ts.tv_sec = MSEC_TO_SEC(timeout_ms); 73 ts.tv_nsec = (timeout_ms % 1000) * 1000000; 74 int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts)); 75 int saved_errno = errno; 76 77 // Set the signals back the way they were. 78 if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) { 79 printf("*** sigprocmask failed: %s\n", strerror(errno)); 80 if (ret == 0) { 81 return false; 82 } 83 } 84 if (ret == -1) { 85 errno = saved_errno; 86 if (errno == EAGAIN) { 87 errno = ETIMEDOUT; 88 } else { 89 printf("*** sigtimedwait failed: %s\n", strerror(errno)); 90 } 91 return false; 92 } 93 94 child_pid = waitpid(pid, status, WNOHANG); 95 if (child_pid != pid) { 96 if (child_pid != -1) { 97 printf("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid); 98 } else { 99 printf("*** waitpid failed: %s\n", strerror(errno)); 100 } 101 return false; 102 } 103 return true; 104 } 105 } // unnamed namespace 106 107 CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build(); 108 CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build(); 109 CommandOptionsBuilder(int64_t timeout_ms)110 CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout_ms) : values(timeout_ms) { 111 } 112 Always()113 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() { 114 values.always_ = true; 115 return *this; 116 } 117 AsRoot()118 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() { 119 if (!PropertiesHelper::IsUnroot()) { 120 values.account_mode_ = SU_ROOT; 121 } 122 return *this; 123 } 124 AsRootIfAvailable()125 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() { 126 if (!PropertiesHelper::IsUserBuild()) { 127 return AsRoot(); 128 } 129 return *this; 130 } 131 DropRoot()132 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() { 133 values.account_mode_ = DROP_ROOT; 134 return *this; 135 } 136 RedirectStderr()137 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() { 138 values.output_mode_ = REDIRECT_TO_STDERR; 139 return *this; 140 } 141 142 CommandOptions::CommandOptionsBuilder& CloseAllFileDescriptorsOnExec()143 CommandOptions::CommandOptionsBuilder::CloseAllFileDescriptorsOnExec() { 144 values.close_all_fds_on_exec_ = true; 145 return *this; 146 } 147 Log(const std::string & message)148 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log( 149 const std::string& message) { 150 values.logging_message_ = message; 151 return *this; 152 } 153 Build()154 CommandOptions CommandOptions::CommandOptionsBuilder::Build() { 155 return CommandOptions(values); 156 } 157 CommandOptionsValues(int64_t timeout_ms)158 CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout_ms) 159 : timeout_ms_(timeout_ms), 160 always_(false), 161 close_all_fds_on_exec_(false), 162 account_mode_(DONT_DROP_ROOT), 163 output_mode_(NORMAL_OUTPUT), 164 logging_message_("") { 165 } 166 CommandOptions(const CommandOptionsValues & values)167 CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) { 168 } 169 Timeout() const170 int64_t CommandOptions::Timeout() const { 171 return MSEC_TO_SEC(values.timeout_ms_); 172 } 173 TimeoutInMs() const174 int64_t CommandOptions::TimeoutInMs() const { 175 return values.timeout_ms_; 176 } 177 Always() const178 bool CommandOptions::Always() const { 179 return values.always_; 180 } 181 ShouldCloseAllFileDescriptorsOnExec() const182 bool CommandOptions::ShouldCloseAllFileDescriptorsOnExec() const { 183 return values.close_all_fds_on_exec_; 184 } 185 PrivilegeMode() const186 PrivilegeMode CommandOptions::PrivilegeMode() const { 187 return values.account_mode_; 188 } 189 OutputMode() const190 OutputMode CommandOptions::OutputMode() const { 191 return values.output_mode_; 192 } 193 LoggingMessage() const194 std::string CommandOptions::LoggingMessage() const { 195 return values.logging_message_; 196 } 197 WithTimeout(int64_t timeout_sec)198 CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout_sec) { 199 return CommandOptions::CommandOptionsBuilder(SEC_TO_MSEC(timeout_sec)); 200 } 201 WithTimeoutInMs(int64_t timeout_ms)202 CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeoutInMs(int64_t timeout_ms) { 203 return CommandOptions::CommandOptionsBuilder(timeout_ms); 204 } 205 206 std::string PropertiesHelper::build_type_ = ""; 207 int PropertiesHelper::dry_run_ = -1; 208 int PropertiesHelper::unroot_ = -1; 209 int PropertiesHelper::parallel_run_ = -1; 210 int PropertiesHelper::strict_run_ = -1; 211 IsUserBuild()212 bool PropertiesHelper::IsUserBuild() { 213 if (build_type_.empty()) { 214 build_type_ = android::base::GetProperty("ro.build.type", "user"); 215 } 216 return "user" == build_type_; 217 } 218 IsDryRun()219 bool PropertiesHelper::IsDryRun() { 220 if (dry_run_ == -1) { 221 dry_run_ = android::base::GetBoolProperty("dumpstate.dry_run", false) ? 1 : 0; 222 } 223 return dry_run_ == 1; 224 } 225 IsUnroot()226 bool PropertiesHelper::IsUnroot() { 227 if (unroot_ == -1) { 228 unroot_ = android::base::GetBoolProperty("dumpstate.unroot", false) ? 1 : 0; 229 } 230 return unroot_ == 1; 231 } 232 IsParallelRun()233 bool PropertiesHelper::IsParallelRun() { 234 if (parallel_run_ == -1) { 235 parallel_run_ = android::base::GetBoolProperty("dumpstate.parallel_run", 236 /* default_value = */true) ? 1 : 0; 237 } 238 return parallel_run_ == 1; 239 } 240 IsStrictRun()241 bool PropertiesHelper::IsStrictRun() { 242 if (strict_run_ == -1) { 243 // Defaults to using stricter timeouts. 244 strict_run_ = android::base::GetBoolProperty("dumpstate.strict_run", true) ? 1 : 0; 245 } 246 return strict_run_ == 1; 247 } 248 DumpFileToFd(int out_fd,const std::string & title,const std::string & path)249 int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { 250 android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); 251 if (fd.get() < 0) { 252 int err = errno; 253 if (title.empty()) { 254 dprintf(out_fd, "*** Error dumping %s: %s\n", path.c_str(), strerror(err)); 255 } else { 256 dprintf(out_fd, "*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), 257 strerror(err)); 258 } 259 return -1; 260 } 261 return DumpFileFromFdToFd(title, path, fd.get(), out_fd, PropertiesHelper::IsDryRun()); 262 } 263 RunCommandToFd(int fd,const std::string & title,const std::vector<std::string> & full_command,const CommandOptions & options)264 int RunCommandToFd(int fd, const std::string& title, const std::vector<std::string>& full_command, 265 const CommandOptions& options) { 266 if (full_command.empty()) { 267 MYLOGE("No arguments on RunCommandToFd(%s)\n", title.c_str()); 268 return -1; 269 } 270 271 int size = full_command.size() + 1; // null terminated 272 int starting_index = 0; 273 if (options.PrivilegeMode() == SU_ROOT) { 274 starting_index = 2; // "su" "root" 275 size += starting_index; 276 } 277 278 std::vector<const char*> args; 279 args.resize(size); 280 281 std::string command_string; 282 if (options.PrivilegeMode() == SU_ROOT) { 283 args[0] = kSuPath; 284 command_string += kSuPath; 285 args[1] = "root"; 286 command_string += " root "; 287 } 288 for (size_t i = 0; i < full_command.size(); i++) { 289 args[i + starting_index] = full_command[i].data(); 290 command_string += args[i + starting_index]; 291 if (i != full_command.size() - 1) { 292 command_string += " "; 293 } 294 } 295 args[size - 1] = nullptr; 296 297 const char* command = command_string.c_str(); 298 299 if (options.PrivilegeMode() == SU_ROOT && PropertiesHelper::IsUserBuild()) { 300 dprintf(fd, "Skipping '%s' on user build.\n", command); 301 return 0; 302 } 303 304 if (!title.empty()) { 305 dprintf(fd, "------ %s (%s) ------\n", title.c_str(), command); 306 } 307 308 const std::string& logging_message = options.LoggingMessage(); 309 if (!logging_message.empty()) { 310 MYLOGI(logging_message.c_str(), command_string.c_str()); 311 } 312 313 bool silent = (options.OutputMode() == REDIRECT_TO_STDERR || 314 options.ShouldCloseAllFileDescriptorsOnExec()); 315 bool redirecting_to_fd = STDOUT_FILENO != fd; 316 317 if (PropertiesHelper::IsDryRun() && !options.Always()) { 318 if (!title.empty()) { 319 dprintf(fd, "\t(skipped on dry run)\n"); 320 } else if (redirecting_to_fd) { 321 // There is no title, but we should still print a dry-run message 322 dprintf(fd, "%s: skipped on dry run\n", command_string.c_str()); 323 } 324 return 0; 325 } 326 327 const char* path = args[0]; 328 329 uint64_t start = Nanotime(); 330 pid_t pid = vfork(); 331 332 /* handle error case */ 333 if (pid < 0) { 334 if (!silent) dprintf(fd, "*** fork: %s\n", strerror(errno)); 335 MYLOGE("*** fork: %s\n", strerror(errno)); 336 return pid; 337 } 338 339 /* handle child case */ 340 if (pid == 0) { 341 if (options.PrivilegeMode() == DROP_ROOT && !DropRootUser()) { 342 if (!silent) { 343 dprintf(fd, "*** failed to drop root before running %s: %s\n", command, 344 strerror(errno)); 345 } 346 MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno)); 347 _exit(EXIT_FAILURE); 348 } 349 350 if (options.ShouldCloseAllFileDescriptorsOnExec()) { 351 int devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY)); 352 TEMP_FAILURE_RETRY(dup2(devnull_fd, STDIN_FILENO)); 353 close(devnull_fd); 354 devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY)); 355 TEMP_FAILURE_RETRY(dup2(devnull_fd, STDOUT_FILENO)); 356 TEMP_FAILURE_RETRY(dup2(devnull_fd, STDERR_FILENO)); 357 close(devnull_fd); 358 // This is to avoid leaking FDs that, accidentally, have not been 359 // marked as O_CLOEXEC. Leaking FDs across exec can cause failures 360 // when execing a process that has a SELinux auto_trans rule. 361 // Here we assume that the dumpstate process didn't open more than 362 // 1000 FDs. In theory we could iterate through /proc/self/fd/, but 363 // doing that in a fork-safe way is too complex and not worth it 364 // (opendir()/readdir() do heap allocations and take locks). 365 for (int i = 0; i < 1000; i++) { 366 if (i != STDIN_FILENO && i!= STDOUT_FILENO && i != STDERR_FILENO) { 367 close(i); 368 } 369 } 370 } else if (silent) { 371 // Redirects stdout to stderr 372 TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO)); 373 } else if (redirecting_to_fd) { 374 // Redirect stdout to fd 375 TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO)); 376 close(fd); 377 } 378 379 /* make sure the child dies when dumpstate dies */ 380 prctl(PR_SET_PDEATHSIG, SIGKILL); 381 382 /* just ignore SIGPIPE, will go down with parent's */ 383 struct sigaction sigact; 384 memset(&sigact, 0, sizeof(sigact)); 385 sigact.sa_handler = SIG_IGN; 386 sigaction(SIGPIPE, &sigact, nullptr); 387 388 execvp(path, (char**)args.data()); 389 // execvp's result will be handled after waitpid_with_timeout() below, but 390 // if it failed, it's safer to exit dumpstate. 391 MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno)); 392 // Must call _exit (instead of exit), otherwise it will corrupt the zip 393 // file. 394 _exit(EXIT_FAILURE); 395 } 396 397 /* handle parent case */ 398 int status; 399 bool ret = waitpid_with_timeout(pid, options.TimeoutInMs(), &status); 400 401 uint64_t elapsed = Nanotime() - start; 402 if (!ret) { 403 if (errno == ETIMEDOUT) { 404 if (!silent) 405 dprintf(fd, "*** command '%s' timed out after %.3fs (killing pid %d)\n", command, 406 static_cast<float>(elapsed) / NANOS_PER_SEC, pid); 407 MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command, 408 static_cast<float>(elapsed) / NANOS_PER_SEC, pid); 409 } else { 410 if (!silent) 411 dprintf(fd, "*** command '%s': Error after %.4fs (killing pid %d)\n", command, 412 static_cast<float>(elapsed) / NANOS_PER_SEC, pid); 413 MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", command, 414 static_cast<float>(elapsed) / NANOS_PER_SEC, pid); 415 } 416 kill(pid, SIGTERM); 417 if (!waitpid_with_timeout(pid, 5000, nullptr)) { 418 kill(pid, SIGKILL); 419 if (!waitpid_with_timeout(pid, 5000, nullptr)) { 420 if (!silent) 421 dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n", 422 command, pid); 423 MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid); 424 } 425 } 426 return -1; 427 } 428 429 if (WIFSIGNALED(status)) { 430 if (!silent) 431 dprintf(fd, "*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status)); 432 MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status)); 433 } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { 434 status = WEXITSTATUS(status); 435 if (!silent) dprintf(fd, "*** command '%s' failed: exit code %d\n", command, status); 436 MYLOGE("*** command '%s' failed: exit code %d\n", command, status); 437 } 438 439 return status; 440 } 441 442 } // namespace dumpstate 443 } // namespace os 444 } // namespace android 445