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