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