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