• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 "carbugreportd"
18 
19 #include <android-base/errors.h>
20 #include <android-base/file.h>
21 #include <android-base/macros.h>
22 #include <android-base/properties.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 #include <android-base/unique_fd.h>
26 #include <cutils/sockets.h>
27 #include <gui/SurfaceComposerClient.h>
28 #include <log/log_main.h>
29 #include <private/android_filesystem_config.h>
30 #include <utils/SystemClock.h>
31 #include <ziparchive/zip_writer.h>
32 
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <ftw.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/prctl.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <time.h>
43 #include <unistd.h>
44 
45 #include <string>
46 #include <vector>
47 
48 namespace {
49 // Directory used for keeping temporary files
50 constexpr const char* kTempDirectory = "/data/user_de/0/com.android.shell/temp_bugreport_files";
51 // Socket to write the progress information.
52 constexpr const char* kCarBrProgressSocket = "car_br_progress_socket";
53 // Socket to write the zipped bugreport file.
54 constexpr const char* kCarBrOutputSocket = "car_br_output_socket";
55 // Socket to write the extra bugreport zip file. This zip file contains data that does not exist
56 // in bugreport file generated by dumpstate.
57 constexpr const char* kCarBrExtraOutputSocket = "car_br_extra_output_socket";
58 // The prefix used by bugreportz protocol to indicate bugreport finished successfully.
59 constexpr const char* kOkPrefix = "OK:";
60 // Number of connect attempts to dumpstate socket
61 constexpr const int kMaxDumpstateConnectAttempts = 20;
62 // Wait time between connect attempts
63 constexpr const int kWaitTimeBetweenConnectAttemptsInSec = 1;
64 // Wait time for dumpstate. Set a timeout so that if nothing is read in 10 minutes, we'll stop
65 // reading and quit. No timeout in dumpstate is longer than 60 seconds, so this gives lots of leeway
66 // in case of unforeseen time outs.
67 constexpr const int kDumpstateTimeoutInSec = 600;
68 // The prefix for screenshot filename in the generated zip file.
69 constexpr const char* kScreenshotPrefix = "/screenshot";
70 
71 using android::OK;
72 using android::PhysicalDisplayId;
73 using android::status_t;
74 using android::SurfaceComposerClient;
75 
76 // Returns a valid socket descriptor or -1 on failure.
openSocket(const char * service)77 int openSocket(const char* service) {
78     int s = android_get_control_socket(service);
79     if (s < 0) {
80         ALOGE("android_get_control_socket(%s): %s", service, strerror(errno));
81         return -1;
82     }
83     fcntl(s, F_SETFD, FD_CLOEXEC);
84     if (listen(s, 4) < 0) {
85         ALOGE("listen(control socket): %s", strerror(errno));
86         return -1;
87     }
88 
89     struct sockaddr addr;
90     socklen_t alen = sizeof(addr);
91     int fd = accept(s, &addr, &alen);
92     if (fd < 0) {
93         ALOGE("accept(control socket): %s", strerror(errno));
94         return -1;
95     }
96     return fd;
97 }
98 
99 // Processes the given dumpstate progress protocol |line| and updates
100 // |out_last_nonempty_line| when |line| is non-empty, and |out_zip_path| when
101 // the bugreport is finished.
processLine(const std::string & line,std::string * out_zip_path,std::string * out_last_nonempty_line)102 void processLine(const std::string& line, std::string* out_zip_path,
103                  std::string* out_last_nonempty_line) {
104     // The protocol is documented in frameworks/native/cmds/bugreportz/readme.md
105     if (line.empty()) {
106         return;
107     }
108     *out_last_nonempty_line = line;
109     if (line.find(kOkPrefix) != 0) {
110         return;
111     }
112     *out_zip_path = line.substr(strlen(kOkPrefix));
113     return;
114 }
115 
116 // Sends the contents of the zip fileto |outfd|.
117 // Returns true if success
zipFilesToFd(const std::vector<std::string> & extra_files,int outfd)118 void zipFilesToFd(const std::vector<std::string>& extra_files, int outfd) {
119     // pass fclose as Deleter to close the file when unique_ptr is destroyed.
120     std::unique_ptr<FILE, decltype(fclose)*> outfile = {fdopen(outfd, "wb"), fclose};
121     if (outfile == nullptr) {
122         ALOGE("Failed to open output descriptor");
123         return;
124     }
125     auto writer = std::make_unique<ZipWriter>(outfile.get());
126 
127     int error = 0;
128     for (const auto& filepath : extra_files) {
129         const auto name = android::base::Basename(filepath);
130 
131         error = writer->StartEntry(name.c_str(), 0);
132         if (error) {
133             ALOGE("Failed to start entry: [%d] %s", error, writer->ErrorCodeString(error));
134             return;
135         }
136         android::base::unique_fd fd(
137                 TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NOFOLLOW)));
138         if (fd == -1) {
139             return;
140         }
141         while (1) {
142             char buffer[65536];
143 
144             ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
145             if (bytes_read == 0) {
146                 break;
147             }
148             if (bytes_read == -1) {
149                 if (errno == EAGAIN) {
150                     ALOGE("timed out while reading %s", name.c_str());
151                 } else {
152                     ALOGE("read terminated abnormally (%s)", strerror(errno));
153                 }
154                 // fail immediately
155                 return;
156             }
157             error = writer->WriteBytes(buffer, bytes_read);
158             if (error) {
159                 ALOGE("WriteBytes() failed: [%d] %s", error, ZipWriter::ErrorCodeString(error));
160                 // fail immediately
161                 return;
162             }
163         }
164 
165         error = writer->FinishEntry();
166         if (error) {
167             ALOGW("failed to finish entry: [%d] %s", error, writer->ErrorCodeString(error));
168             continue;
169         }
170     }
171     error = writer->Finish();
172     if (error) {
173         ALOGW("Failed to finish zip writer to: [%d] %s", error, writer->ErrorCodeString(error));
174     }
175 }
176 
copyTo(int fd_in,int fd_out,void * buffer,size_t buffer_len)177 int copyTo(int fd_in, int fd_out, void* buffer, size_t buffer_len) {
178     ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd_in, buffer, buffer_len));
179     if (bytes_read == 0) {
180         return 0;
181     }
182     if (bytes_read == -1) {
183         // EAGAIN really means time out, so make that clear.
184         if (errno == EAGAIN) {
185             ALOGE("read timed out");
186         } else {
187             ALOGE("read terminated abnormally (%s)", strerror(errno));
188         }
189         return -1;
190     }
191     // copy all bytes to the output socket
192     if (!android::base::WriteFully(fd_out, buffer, bytes_read)) {
193         ALOGE("write failed");
194         return -1;
195     }
196     return bytes_read;
197 }
198 
copyFile(const std::string & zip_path,int output_socket)199 bool copyFile(const std::string& zip_path, int output_socket) {
200     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(zip_path.c_str(), O_RDONLY | O_NOFOLLOW)));
201     if (fd == -1) {
202         ALOGE("Failed to open zip file %s.", zip_path.c_str());
203         return false;
204     }
205     while (1) {
206         char buffer[65536];
207         int bytes_copied = copyTo(fd, output_socket, buffer, sizeof(buffer));
208         if (bytes_copied == 0) {
209             break;
210         }
211         if (bytes_copied == -1) {
212             ALOGE("Failed to copy zip file %s to the output_socket.", zip_path.c_str());
213             return false;
214         }
215     }
216     return true;
217 }
218 
219 // Triggers a bugreport and waits until it is all collected.
220 // returns false if error, true if success
doBugreport(int progress_socket,size_t * out_bytes_written,std::string * zip_path)221 bool doBugreport(int progress_socket, size_t* out_bytes_written, std::string* zip_path) {
222     // Socket will not be available until service starts.
223     android::base::unique_fd s;
224     for (int i = 0; i < kMaxDumpstateConnectAttempts; i++) {
225         s.reset(socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
226         if (s != -1) break;
227         sleep(kWaitTimeBetweenConnectAttemptsInSec);
228     }
229 
230     if (s == -1) {
231         ALOGE("failed to connect to dumpstatez service");
232         return false;
233     }
234 
235     // Set a timeout so that if nothing is read by the timeout, stop reading and quit
236     struct timeval tv = {
237             .tv_sec = kDumpstateTimeoutInSec,
238             .tv_usec = 0,
239     };
240     if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
241         ALOGW("Cannot set socket timeout (%s)", strerror(errno));
242     }
243 
244     std::string line;
245     std::string last_nonempty_line;
246     char buffer[65536];
247     while (true) {
248         ssize_t bytes_read = copyTo(s, progress_socket, buffer, sizeof(buffer));
249         if (bytes_read == 0) {
250             break;
251         }
252         if (bytes_read == -1) {
253             ALOGE("Failed to copy progress to the progress_socket.");
254             return false;
255         }
256         // Process the buffer line by line. this is needed for the filename.
257         for (int i = 0; i < bytes_read; i++) {
258             char c = buffer[i];
259             if (c == '\n') {
260                 processLine(line, zip_path, &last_nonempty_line);
261                 line.clear();
262             } else {
263                 line.append(1, c);
264             }
265         }
266         *out_bytes_written += bytes_read;
267     }
268     s.reset();
269     // Process final line, in case it didn't finish with newline.
270     processLine(line, zip_path, &last_nonempty_line);
271     // if doBugReport finished successfully, zip path should be set.
272     if (zip_path->empty()) {
273         ALOGE("no zip file path was found in bugreportz progress data");
274         return false;
275     }
276     return true;
277 }
278 
waitpid_with_timeout(pid_t pid,int timeout_secs,int * status)279 bool waitpid_with_timeout(pid_t pid, int timeout_secs, int* status) {
280     sigset_t child_mask, old_mask;
281     sigemptyset(&child_mask);
282     sigaddset(&child_mask, SIGCHLD);
283 
284     if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
285         ALOGE("*** sigprocmask failed: %s\n", strerror(errno));
286         return false;
287     }
288 
289     timespec ts = {.tv_sec = timeout_secs, .tv_nsec = 0};
290     int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts));
291     int saved_errno = errno;
292 
293     // Set the signals back the way they were.
294     if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) {
295         ALOGE("*** sigprocmask failed: %s\n", strerror(errno));
296         if (ret == 0) {
297             return false;
298         }
299     }
300     if (ret == -1) {
301         errno = saved_errno;
302         if (errno == EAGAIN) {
303             errno = ETIMEDOUT;
304         } else {
305             ALOGE("*** sigtimedwait failed: %s\n", strerror(errno));
306         }
307         return false;
308     }
309 
310     pid_t child_pid = waitpid(pid, status, WNOHANG);
311     if (child_pid != pid) {
312         if (child_pid != -1) {
313             ALOGE("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
314         } else {
315             ALOGE("*** waitpid failed: %s\n", strerror(errno));
316         }
317         return false;
318     }
319     return true;
320 }
321 
322 // Runs the given command. Kills the command if it does not finish by timeout.
runCommand(int timeout_secs,const char * file,std::vector<const char * > args)323 int runCommand(int timeout_secs, const char* file, std::vector<const char*> args) {
324     pid_t pid = fork();
325 
326     // handle error case
327     if (pid < 0) {
328         ALOGE("fork failed %s", strerror(errno));
329         return pid;
330     }
331 
332     // handle child case
333     if (pid == 0) {
334         /* make sure the child dies when parent dies */
335         prctl(PR_SET_PDEATHSIG, SIGKILL);
336 
337         /* just ignore SIGPIPE, will go down with parent's */
338         struct sigaction sigact;
339         memset(&sigact, 0, sizeof(sigact));
340         sigact.sa_handler = SIG_IGN;
341         sigaction(SIGPIPE, &sigact, nullptr);
342 
343         execvp(file, const_cast<char* const*>(args.data()));
344         // execvp's result will be handled after waitpid_with_timeout() below, but
345         // if it failed, it's safer to exit dumpstate.
346         ALOGE("execvp on command %s failed (error: %s)", file, strerror(errno));
347         _exit(EXIT_FAILURE);
348     }
349 
350     // handle parent case
351     int status;
352     bool ret = waitpid_with_timeout(pid, timeout_secs, &status);
353 
354     if (!ret) {
355         if (errno == ETIMEDOUT) {
356             ALOGE("command %s timed out (killing pid %d)", file, pid);
357         } else {
358             ALOGE("command %s: Error (killing pid %d)\n", file, pid);
359         }
360         kill(pid, SIGTERM);
361         if (!waitpid_with_timeout(pid, 5, nullptr)) {
362             kill(pid, SIGKILL);
363             if (!waitpid_with_timeout(pid, 5, nullptr)) {
364                 ALOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", file, pid);
365             }
366         }
367         return -1;
368     }
369 
370     if (WIFSIGNALED(status)) {
371         ALOGE("command '%s' failed: killed by signal %d\n", file, WTERMSIG(status));
372     } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
373         status = WEXITSTATUS(status);
374         ALOGE("command '%s' failed: exit code %d\n", file, status);
375     }
376 
377     return status;
378 }
379 
takeScreenshotForDisplayId(PhysicalDisplayId id,const char * tmp_dir,std::vector<std::string> * extra_files)380 void takeScreenshotForDisplayId(PhysicalDisplayId id, const char* tmp_dir,
381                                 std::vector<std::string>* extra_files) {
382     std::string id_as_string = to_string(id);
383     std::string filename = std::string(tmp_dir) + kScreenshotPrefix + id_as_string + ".png";
384     std::vector<const char*> args{"-p", "-d", id_as_string.c_str(), filename.c_str(), nullptr};
385     ALOGI("capturing screen for display (%s) as %s", id_as_string.c_str(), filename.c_str());
386     int status = runCommand(10, "/system/bin/screencap", args);
387     if (status == 0) {
388         ALOGI("Screenshot saved for display: %s", id_as_string.c_str());
389     }
390     // add the file regardless of the exit status of the screencap util.
391     extra_files->push_back(filename);
392 
393     ALOGW("Failed to take screenshot for display: %s", id_as_string.c_str());
394 }
395 
takeScreenshot(const char * tmp_dir,std::vector<std::string> * extra_files)396 void takeScreenshot(const char* tmp_dir, std::vector<std::string>* extra_files) {
397     // Now send the screencaptures
398     std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
399 
400     for (PhysicalDisplayId display_id : ids) {
401         takeScreenshotForDisplayId(display_id, tmp_dir, extra_files);
402     }
403 }
404 
recursiveRemoveDir(const std::string & path)405 bool recursiveRemoveDir(const std::string& path) {
406     auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
407         if (file_type == FTW_DP) {
408             if (rmdir(child) == -1) {
409                 ALOGE("rmdir(%s): %s", child, strerror(errno));
410                 return -1;
411             }
412         } else if (file_type == FTW_F) {
413             if (unlink(child) == -1) {
414                 ALOGE("unlink(%s): %s", child, strerror(errno));
415                 return -1;
416             }
417         }
418         return 0;
419     };
420     // do a file tree walk with a sufficiently large depth.
421     return nftw(path.c_str(), callback, 128, FTW_DEPTH) == 0;
422 }
423 
createTempDir(const char * dir)424 status_t createTempDir(const char* dir) {
425     struct stat sb;
426     if (TEMP_FAILURE_RETRY(stat(dir, &sb)) == 0) {
427         if (!recursiveRemoveDir(dir)) {
428             return -errno;
429         }
430     } else if (errno != ENOENT) {
431         ALOGE("Failed to stat %s ", dir);
432         return -errno;
433     }
434     if (TEMP_FAILURE_RETRY(mkdir(dir, 0700)) == -1) {
435         ALOGE("Failed to mkdir %s", dir);
436         return -errno;
437     }
438     return OK;
439 }
440 
441 // Removes bugreport
cleanupBugreportFile(const std::string & zip_path)442 void cleanupBugreportFile(const std::string& zip_path) {
443     if (unlink(zip_path.c_str()) != 0) {
444         ALOGE("Could not unlink %s (%s)", zip_path.c_str(), strerror(errno));
445     }
446 }
447 
448 }  // namespace
449 
main(void)450 int main(void) {
451     ALOGI("Starting bugreport collecting service");
452 
453     auto started_at_millis = android::elapsedRealtime();
454 
455     std::vector<std::string> extra_files;
456     if (createTempDir(kTempDirectory) == OK) {
457         // take screenshots of the physical displays as early as possible
458         takeScreenshot(kTempDirectory, &extra_files);
459     }
460 
461     // Start the dumpstatez service.
462     android::base::SetProperty("ctl.start", "cardumpstatez");
463 
464     size_t bytes_written = 0;
465 
466     std::string zip_path;
467     int progress_socket = openSocket(kCarBrProgressSocket);
468     if (progress_socket < 0) {
469         // early out. in this case we will not print the final message, but that is ok.
470         android::base::SetProperty("ctl.stop", "cardumpstatez");
471         return EXIT_FAILURE;
472     }
473     bool is_success = doBugreport(progress_socket, &bytes_written, &zip_path);
474     close(progress_socket);
475 
476     if (is_success) {
477         int output_socket = openSocket(kCarBrOutputSocket);
478         if (output_socket != -1) {
479             is_success = copyFile(zip_path, output_socket);
480             close(output_socket);
481         }
482     }
483 
484     int extra_output_socket = openSocket(kCarBrExtraOutputSocket);
485     if (extra_output_socket != -1 && is_success) {
486         zipFilesToFd(extra_files, extra_output_socket);
487     }
488     if (extra_output_socket != -1) {
489         close(extra_output_socket);
490     }
491 
492     auto delta_sec = (android::elapsedRealtime() - started_at_millis) / 1000.0;
493     std::string result = is_success ? "success" : "failed";
494     ALOGI("bugreport %s in %.02fs, %zu bytes written", result.c_str(), delta_sec, bytes_written);
495     cleanupBugreportFile(zip_path);
496 
497     recursiveRemoveDir(kTempDirectory);
498 
499     // No matter how doBugreport() finished, let's try to explicitly stop
500     // cardumpstatez in case it stalled.
501     android::base::SetProperty("ctl.stop", "cardumpstatez");
502 
503     return is_success ? EXIT_SUCCESS : EXIT_FAILURE;
504 }
505