Loading cmds/bugreportz/bugreportz.cpp +28 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,10 @@ * limitations under the License. */ #include "bugreportz.h" #include <android-base/file.h> #include <android-base/strings.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> Loading @@ -22,11 +26,6 @@ #include <string> #include <android-base/file.h> #include <android-base/strings.h> #include "bugreportz.h" static constexpr char BEGIN_PREFIX[] = "BEGIN:"; static constexpr char PROGRESS_PREFIX[] = "PROGRESS:"; Loading Loading @@ -70,6 +69,30 @@ int bugreportz(int s, bool show_progress) { } // Process final line, in case it didn't finish with newline write_line(line, show_progress); return EXIT_SUCCESS; } int bugreportz_stream(int s) { while (1) { char buffer[65536]; ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer))); if (bytes_read == 0) { break; } else if (bytes_read == -1) { // EAGAIN really means time out, so change the errno. if (errno == EAGAIN) { errno = ETIMEDOUT; } printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno)); return EXIT_FAILURE; } if (!android::base::WriteFully(android::base::borrowed_fd(STDOUT_FILENO), buffer, bytes_read)) { printf("Failed to write data to stdout: trying to send %zd bytes (%s)\n", bytes_read, strerror(errno)); return EXIT_FAILURE; } } return EXIT_SUCCESS; } cmds/bugreportz/bugreportz.h +4 −0 Original line number Diff line number Diff line Loading @@ -19,4 +19,8 @@ // Ownership of the socket is not transferred. int bugreportz(int s, bool show_progress); // Calls dumpstate using the given socket and write the file content to stdout // instead of file location. Ownership of the socket is not transferred. int bugreportz_stream(int s); #endif // BUGREPORTZ_H cmds/bugreportz/main.cpp +19 −5 Original line number Diff line number Diff line Loading @@ -26,13 +26,14 @@ #include "bugreportz.h" static constexpr char VERSION[] = "1.1"; static constexpr char VERSION[] = "1.2"; static void show_usage() { fprintf(stderr, "usage: bugreportz [-hpv]\n" "usage: bugreportz [-hpsv]\n" " -h: to display this help message\n" " -p: display progress\n" " -s: stream content to standard output\n" " -v: to display the version\n" " or no arguments to generate a zipped bugreport\n"); } Loading @@ -43,10 +44,11 @@ static void show_version() { int main(int argc, char* argv[]) { bool show_progress = false; bool stream_data = false; if (argc > 1) { /* parse arguments */ int c; while ((c = getopt(argc, argv, "hpv")) != -1) { while ((c = getopt(argc, argv, "hpsv")) != -1) { switch (c) { case 'h': show_usage(); Loading @@ -54,6 +56,9 @@ int main(int argc, char* argv[]) { case 'p': show_progress = true; break; case 's': stream_data = true; break; case 'v': show_version(); return EXIT_SUCCESS; Loading @@ -75,7 +80,11 @@ int main(int argc, char* argv[]) { // should be reused instead. // Start the dumpstatez service. if (stream_data) { property_set("ctl.start", "dumpstate"); } else { property_set("ctl.start", "dumpstatez"); } // Socket will not be available until service starts. int s = -1; Loading Loading @@ -103,7 +112,12 @@ int main(int argc, char* argv[]) { strerror(errno)); } int ret = bugreportz(s, show_progress); int ret; if (stream_data) { ret = bugreportz_stream(s); } else { ret = bugreportz(s, show_progress); } if (close(s) == -1) { fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); Loading cmds/dumpstate/dumpstate.cpp +96 −144 Original line number Diff line number Diff line Loading @@ -719,6 +719,9 @@ static unsigned long logcat_timeout(const std::vector<std::string>& buffers) { return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS; } // Opens a socket and returns its file descriptor. static int open_socket(const char* service); Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) { } Loading Loading @@ -2286,20 +2289,18 @@ void Dumpstate::DumpstateBoard(int out_fd) { static void ShowUsage() { fprintf(stderr, "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] " "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n" "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-p] " "[-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n" " -h: display this help message\n" " -b: play sound file instead of vibrate, at beginning of job\n" " -e: play sound file instead of vibrate, at end of job\n" " -o: write to custom directory (only in limited mode)\n" " -d: append date to filename\n" " -p: capture screenshot to filename.png\n" " -z: generate zipped file\n" " -s: write output to control socket (for init)\n" " -S: write file location to control socket (for init; requires -z)\n" " -s: write zipped file to control socket (for init)\n" " -S: write file location to control socket (for init)\n" " -q: disable vibrate\n" " -P: send broadcast when started and do progress updates\n" " -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n" " -R: take bugreport in remote mode (shouldn't be used with -P)\n" " -w: start binder service and make it wait for a call to startBugreport\n" " -L: output limited information that is safe for submission in feedback reports\n" " -v: prints the dumpstate header and exit\n"); Loading Loading @@ -2398,21 +2399,17 @@ static void MaybeResolveSymlink(std::string* path) { /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter * if we are writing zip files and adds the version file. * and adds the version file. Return false if zip_file could not be open to write. */ static void PrepareToWriteToFile() { static bool PrepareToWriteToFile() { MaybeResolveSymlink(&ds.bugreport_internal_dir_); std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE"); ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str()); if (ds.options_->do_add_date) { char date[80]; strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); ds.name_ = date; } else { ds.name_ = "undated"; } if (ds.options_->telephony_only) { ds.base_name_ += "-telephony"; Loading @@ -2439,18 +2436,17 @@ static void PrepareToWriteToFile() { destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); if (ds.options_->do_zip_file) { ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip"); MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); create_parent_dirs(ds.path_.c_str()); ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); if (ds.zip_file == nullptr) { MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno)); } else { ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); return false; } ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); ds.AddTextZipEntry("version.txt", ds.version_); } return true; } /* Loading @@ -2458,14 +2454,9 @@ static void PrepareToWriteToFile() { * printing zipped file status, etc. */ static void FinalizeFile() { bool do_text_file = true; if (ds.options_->do_zip_file) { if (!ds.FinishZipFile()) { bool do_text_file = !ds.FinishZipFile(); if (do_text_file) { MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); do_text_file = true; } else { do_text_file = false; } } std::string final_path = ds.path_; Loading @@ -2474,7 +2465,9 @@ static void FinalizeFile() { android::os::CopyFileToFile(ds.path_, final_path); } if (ds.options_->use_control_socket) { if (ds.options_->stream_to_socket) { android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_); } else if (ds.options_->progress_updates_to_socket) { if (do_text_file) { dprintf(ds.control_socket_fd_, "FAIL:could not create zip file, check %s " Loading Loading @@ -2530,7 +2523,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_WEAR: options->do_progress_updates = true; options->do_zip_file = true; options->do_screenshot = is_screenshot_requested; options->dumpstate_hal_mode = DumpstateMode::WEAR; break; Loading @@ -2543,7 +2535,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_WIFI: options->wifi_only = true; options->do_zip_file = true; options->do_screenshot = false; options->dumpstate_hal_mode = DumpstateMode::WIFI; break; Loading @@ -2554,11 +2545,11 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI( "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d " "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d " "is_remote_mode: %d show_header_only: %d telephony_only: %d " "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s " "limited_only: %d args: %s\n", options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket, options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket, options.do_screenshot, options.is_remote_mode, options.show_header_only, options.telephony_only, options.wifi_only, options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(), Loading @@ -2569,11 +2560,6 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd_in, const android::base::unique_fd& screenshot_fd_in, bool is_screenshot_requested) { // In the new API world, date is always added; output is always a zip file. // TODO(111441001): remove these options once they are obsolete. do_add_date = true; do_zip_file = true; // Duplicate the fds because the passed in fds don't outlive the binder transaction. bugreport_fd.reset(dup(bugreport_fd_in.get())); screenshot_fd.reset(dup(screenshot_fd_in.get())); Loading @@ -2587,18 +2573,20 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) { switch (c) { // clang-format off case 'd': do_add_date = true; break; case 'z': do_zip_file = true; break; case 'o': out_dir = optarg; break; case 's': use_socket = true; break; case 'S': use_control_socket = true; break; case 's': stream_to_socket = true; break; case 'S': progress_updates_to_socket = true; break; case 'v': show_header_only = true; break; case 'q': do_vibrate = false; break; case 'p': do_screenshot = true; break; case 'P': do_progress_updates = true; break; case 'R': is_remote_mode = true; break; case 'L': limited_only = true; break; case 'V': break; // compatibility no-op case 'V': case 'd': case 'z': // compatibility no-op break; case 'w': // This was already processed break; Loading Loading @@ -2627,19 +2615,15 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } bool Dumpstate::DumpOptions::ValidateOptions() const { if (bugreport_fd.get() != -1 && !do_zip_file) { if (bugreport_fd.get() != -1 && stream_to_socket) { return false; } if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) { if ((progress_updates_to_socket || do_progress_updates) && stream_to_socket) { return false; } if (use_control_socket && !do_zip_file) { return false; } if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) { if (is_remote_mode && (do_progress_updates || stream_to_socket)) { return false; } return true; Loading Loading @@ -2714,11 +2698,9 @@ void Dumpstate::Cancel() { * The temporary bugreport is then populated via printfs, dumping contents of files and * output of commands to stdout. * * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final * text file. * A bunch of other files and dumps are added to the zip archive. * * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also * gets added to the archive. * The temporary bugreport file and the log file also get added to the archive. * * Bugreports are first generated in a local directory and later copied to the caller's fd * or directory if supplied. Loading Loading @@ -2766,14 +2748,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n", calling_uid, calling_package.c_str()); // Redirect output if needed bool is_redirecting = options_->OutputToFile(); // TODO: temporarily set progress until it's part of the Dumpstate constructor std::string stats_path = is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) : ""; android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()); progress_.reset(new Progress(stats_path)); if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) { Loading @@ -2796,24 +2773,23 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // If we are going to use a socket, do it as early as possible // to avoid timeouts from bugreport. if (options_->use_socket) { if (!redirect_to_socket(stdout, "dumpstate")) { return ERROR; } } if (options_->use_control_socket) { if (options_->stream_to_socket || options_->progress_updates_to_socket) { MYLOGD("Opening control socket\n"); control_socket_fd_ = open_socket("dumpstate"); control_socket_fd_ = open_socket_fn_("dumpstate"); if (control_socket_fd_ == -1) { return ERROR; } if (options_->progress_updates_to_socket) { options_->do_progress_updates = 1; } } if (is_redirecting) { PrepareToWriteToFile(); if (!PrepareToWriteToFile()) { return ERROR; } // Interactive, wear & telephony modes are default to true. // and may enable from cli option or when using control socket if (options_->do_progress_updates) { // clang-format off std::vector<std::string> am_args = { Loading @@ -2822,11 +2798,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // clang-format on // Send STARTED broadcast for apps that listen to bugreport generation events SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); if (options_->use_control_socket) { if (options_->progress_updates_to_socket) { dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str()); } } } /* read /proc/cmdline before dropping root */ FILE *cmdline = fopen("/proc/cmdline", "re"); Loading @@ -2839,7 +2814,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, Vibrate(150); } if (options_->do_zip_file && zip_file != nullptr) { if (zip_file != nullptr) { if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(), strerror(errno)); Loading @@ -2848,7 +2823,6 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, int dup_stdout_fd; int dup_stderr_fd; if (is_redirecting) { // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) { Loading @@ -2873,7 +2847,6 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", tmp_path_.c_str(), strerror(errno)); } } // Don't buffer stdout setvbuf(stdout, nullptr, _IONBF, 0); Loading Loading @@ -2926,14 +2899,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } /* close output if needed */ if (is_redirecting) { TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); } // Zip the (now complete) .tmp file within the internal directory. if (options_->OutputToFile()) { FinalizeFile(); } // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; Loading Loading @@ -2976,11 +2945,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, progress_->Save(); MYLOGI("done (id %d)\n", id_); if (is_redirecting) { TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); } if (options_->use_control_socket && control_socket_fd_ != -1) { if (control_socket_fd_ != -1) { MYLOGD("Closing control socket\n"); close(control_socket_fd_); } Loading Loading @@ -3051,10 +3018,7 @@ void Dumpstate::CleanupTmpFiles() { } void Dumpstate::EnableParallelRunIfNeeded() { // The thread pool needs to create temporary files to receive dump results. // That's why we only enable it when the bugreport client chooses to output // to a file. if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) { if (!PropertiesHelper::IsParallelRun()) { return; } dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_); Loading Loading @@ -3199,7 +3163,8 @@ Dumpstate::Dumpstate(const std::string& version) options_(new Dumpstate::DumpOptions()), last_reported_percent_progress_(0), version_(version), now_(time(nullptr)) { now_(time(nullptr)), open_socket_fn_(open_socket) { } Dumpstate& Dumpstate::GetInstance() { Loading Loading @@ -3833,7 +3798,7 @@ void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::stri RunCommand(title, dumpsys, options, false, out_fd); } int open_socket(const char *service) { static int open_socket(const char* service) { int s = android_get_control_socket(service); if (s < 0) { MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); Loading Loading @@ -3868,19 +3833,6 @@ int open_socket(const char *service) { return fd; } /* redirect output to a service control socket */ bool redirect_to_socket(FILE* redirect, const char* service) { int fd = open_socket(service); if (fd == -1) { return false; } fflush(redirect); // TODO: handle dup2 failure TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); return true; } // TODO: should call is_valid_output_file and/or be merged into it. void create_parent_dirs(const char *path) { char *chp = const_cast<char *> (path); Loading cmds/dumpstate/dumpstate.h +12 −25 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ namespace dumpstate { class DumpstateTest; class ProgressTest; class ZippedBugReportStreamTest; } // namespace dumpstate } // namespace os Loading Loading @@ -197,6 +198,7 @@ struct DumpData { */ class Dumpstate { friend class android::os::dumpstate::DumpstateTest; friend class android::os::dumpstate::ZippedBugReportStreamTest; public: enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT }; Loading Loading @@ -388,12 +390,11 @@ class Dumpstate { * Structure to hold options that determine the behavior of dumpstate. */ struct DumpOptions { bool do_add_date = false; bool do_zip_file = false; bool do_vibrate = true; // Writes bugreport content to a socket; only flatfile format is supported. bool use_socket = false; bool use_control_socket = false; // Writes bugreport zipped file to a socket. bool stream_to_socket = false; // Writes generation progress updates to a socket. bool progress_updates_to_socket = false; bool do_screenshot = false; bool is_screenshot_copied = false; bool is_remote_mode = false; Loading Loading @@ -434,13 +435,6 @@ class Dumpstate { /* Returns true if the options set so far are consistent. */ bool ValidateOptions() const; /* Returns if options specified require writing bugreport to a file */ bool OutputToFile() const { // If we are not writing to socket, we will write to a file. If bugreport_fd is // specified, it is preferred. If not bugreport is written to /bugreports. return !use_socket; } /* Returns if options specified require writing to custom file location */ bool OutputToCustomFile() { // Custom location is only honored in limited mode. Loading @@ -466,7 +460,8 @@ class Dumpstate { std::unique_ptr<Progress> progress_; // When set, defines a socket file-descriptor use to report progress to bugreportz. // When set, defines a socket file-descriptor use to report progress to bugreportz // or to stream the zipped file to. int control_socket_fd_ = -1; // Bugreport format version; Loading @@ -478,8 +473,8 @@ class Dumpstate { // `bugreport-BUILD_ID`. std::string base_name_; // Name is the suffix part of the bugreport files - it's typically the date (when invoked with // `-d`), but it could be changed by the user.. // Name is the suffix part of the bugreport files - it's typically the date, // but it could be changed by the user.. std::string name_; std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; Loading Loading @@ -568,6 +563,8 @@ class Dumpstate { // called by Shell. RunStatus CopyBugreportIfUserConsented(int32_t calling_uid); std::function<int(const char *)> open_socket_fn_; // Used by GetInstance() only. explicit Dumpstate(const std::string& version = VERSION_CURRENT); Loading Loading @@ -601,16 +598,6 @@ int dump_file_from_fd(const char *title, const char *path, int fd); int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), int (*dump_from_fd)(const char* title, const char* path, int fd)); /** opens a socket and returns its file descriptor */ int open_socket(const char *service); /* * Redirects 'redirect' to a service control socket. * * Returns true if redirect succeeds. */ bool redirect_to_socket(FILE* redirect, const char* service); /* * Redirects 'redirect' to a file indicated by 'path', truncating it. * Loading Loading
cmds/bugreportz/bugreportz.cpp +28 −5 Original line number Diff line number Diff line Loading @@ -14,6 +14,10 @@ * limitations under the License. */ #include "bugreportz.h" #include <android-base/file.h> #include <android-base/strings.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> Loading @@ -22,11 +26,6 @@ #include <string> #include <android-base/file.h> #include <android-base/strings.h> #include "bugreportz.h" static constexpr char BEGIN_PREFIX[] = "BEGIN:"; static constexpr char PROGRESS_PREFIX[] = "PROGRESS:"; Loading Loading @@ -70,6 +69,30 @@ int bugreportz(int s, bool show_progress) { } // Process final line, in case it didn't finish with newline write_line(line, show_progress); return EXIT_SUCCESS; } int bugreportz_stream(int s) { while (1) { char buffer[65536]; ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer))); if (bytes_read == 0) { break; } else if (bytes_read == -1) { // EAGAIN really means time out, so change the errno. if (errno == EAGAIN) { errno = ETIMEDOUT; } printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno)); return EXIT_FAILURE; } if (!android::base::WriteFully(android::base::borrowed_fd(STDOUT_FILENO), buffer, bytes_read)) { printf("Failed to write data to stdout: trying to send %zd bytes (%s)\n", bytes_read, strerror(errno)); return EXIT_FAILURE; } } return EXIT_SUCCESS; }
cmds/bugreportz/bugreportz.h +4 −0 Original line number Diff line number Diff line Loading @@ -19,4 +19,8 @@ // Ownership of the socket is not transferred. int bugreportz(int s, bool show_progress); // Calls dumpstate using the given socket and write the file content to stdout // instead of file location. Ownership of the socket is not transferred. int bugreportz_stream(int s); #endif // BUGREPORTZ_H
cmds/bugreportz/main.cpp +19 −5 Original line number Diff line number Diff line Loading @@ -26,13 +26,14 @@ #include "bugreportz.h" static constexpr char VERSION[] = "1.1"; static constexpr char VERSION[] = "1.2"; static void show_usage() { fprintf(stderr, "usage: bugreportz [-hpv]\n" "usage: bugreportz [-hpsv]\n" " -h: to display this help message\n" " -p: display progress\n" " -s: stream content to standard output\n" " -v: to display the version\n" " or no arguments to generate a zipped bugreport\n"); } Loading @@ -43,10 +44,11 @@ static void show_version() { int main(int argc, char* argv[]) { bool show_progress = false; bool stream_data = false; if (argc > 1) { /* parse arguments */ int c; while ((c = getopt(argc, argv, "hpv")) != -1) { while ((c = getopt(argc, argv, "hpsv")) != -1) { switch (c) { case 'h': show_usage(); Loading @@ -54,6 +56,9 @@ int main(int argc, char* argv[]) { case 'p': show_progress = true; break; case 's': stream_data = true; break; case 'v': show_version(); return EXIT_SUCCESS; Loading @@ -75,7 +80,11 @@ int main(int argc, char* argv[]) { // should be reused instead. // Start the dumpstatez service. if (stream_data) { property_set("ctl.start", "dumpstate"); } else { property_set("ctl.start", "dumpstatez"); } // Socket will not be available until service starts. int s = -1; Loading Loading @@ -103,7 +112,12 @@ int main(int argc, char* argv[]) { strerror(errno)); } int ret = bugreportz(s, show_progress); int ret; if (stream_data) { ret = bugreportz_stream(s); } else { ret = bugreportz(s, show_progress); } if (close(s) == -1) { fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); Loading
cmds/dumpstate/dumpstate.cpp +96 −144 Original line number Diff line number Diff line Loading @@ -719,6 +719,9 @@ static unsigned long logcat_timeout(const std::vector<std::string>& buffers) { return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS; } // Opens a socket and returns its file descriptor. static int open_socket(const char* service); Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) { } Loading Loading @@ -2286,20 +2289,18 @@ void Dumpstate::DumpstateBoard(int out_fd) { static void ShowUsage() { fprintf(stderr, "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-d] [-p] " "[-z] [-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n" "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o directory] [-p] " "[-s] [-S] [-q] [-P] [-R] [-L] [-V version]\n" " -h: display this help message\n" " -b: play sound file instead of vibrate, at beginning of job\n" " -e: play sound file instead of vibrate, at end of job\n" " -o: write to custom directory (only in limited mode)\n" " -d: append date to filename\n" " -p: capture screenshot to filename.png\n" " -z: generate zipped file\n" " -s: write output to control socket (for init)\n" " -S: write file location to control socket (for init; requires -z)\n" " -s: write zipped file to control socket (for init)\n" " -S: write file location to control socket (for init)\n" " -q: disable vibrate\n" " -P: send broadcast when started and do progress updates\n" " -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n" " -R: take bugreport in remote mode (shouldn't be used with -P)\n" " -w: start binder service and make it wait for a call to startBugreport\n" " -L: output limited information that is safe for submission in feedback reports\n" " -v: prints the dumpstate header and exit\n"); Loading Loading @@ -2398,21 +2399,17 @@ static void MaybeResolveSymlink(std::string* path) { /* * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter * if we are writing zip files and adds the version file. * and adds the version file. Return false if zip_file could not be open to write. */ static void PrepareToWriteToFile() { static bool PrepareToWriteToFile() { MaybeResolveSymlink(&ds.bugreport_internal_dir_); std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE"); ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str()); if (ds.options_->do_add_date) { char date[80]; strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); ds.name_ = date; } else { ds.name_ = "undated"; } if (ds.options_->telephony_only) { ds.base_name_ += "-telephony"; Loading @@ -2439,18 +2436,17 @@ static void PrepareToWriteToFile() { destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); if (ds.options_->do_zip_file) { ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip"); MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); create_parent_dirs(ds.path_.c_str()); ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); if (ds.zip_file == nullptr) { MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno)); } else { ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); return false; } ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); ds.AddTextZipEntry("version.txt", ds.version_); } return true; } /* Loading @@ -2458,14 +2454,9 @@ static void PrepareToWriteToFile() { * printing zipped file status, etc. */ static void FinalizeFile() { bool do_text_file = true; if (ds.options_->do_zip_file) { if (!ds.FinishZipFile()) { bool do_text_file = !ds.FinishZipFile(); if (do_text_file) { MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); do_text_file = true; } else { do_text_file = false; } } std::string final_path = ds.path_; Loading @@ -2474,7 +2465,9 @@ static void FinalizeFile() { android::os::CopyFileToFile(ds.path_, final_path); } if (ds.options_->use_control_socket) { if (ds.options_->stream_to_socket) { android::os::CopyFileToFd(ds.path_, ds.control_socket_fd_); } else if (ds.options_->progress_updates_to_socket) { if (do_text_file) { dprintf(ds.control_socket_fd_, "FAIL:could not create zip file, check %s " Loading Loading @@ -2530,7 +2523,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_WEAR: options->do_progress_updates = true; options->do_zip_file = true; options->do_screenshot = is_screenshot_requested; options->dumpstate_hal_mode = DumpstateMode::WEAR; break; Loading @@ -2543,7 +2535,6 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_WIFI: options->wifi_only = true; options->do_zip_file = true; options->do_screenshot = false; options->dumpstate_hal_mode = DumpstateMode::WIFI; break; Loading @@ -2554,11 +2545,11 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI( "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d " "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d " "is_remote_mode: %d show_header_only: %d telephony_only: %d " "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s " "limited_only: %d args: %s\n", options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket, options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket, options.do_screenshot, options.is_remote_mode, options.show_header_only, options.telephony_only, options.wifi_only, options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(), Loading @@ -2569,11 +2560,6 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd_in, const android::base::unique_fd& screenshot_fd_in, bool is_screenshot_requested) { // In the new API world, date is always added; output is always a zip file. // TODO(111441001): remove these options once they are obsolete. do_add_date = true; do_zip_file = true; // Duplicate the fds because the passed in fds don't outlive the binder transaction. bugreport_fd.reset(dup(bugreport_fd_in.get())); screenshot_fd.reset(dup(screenshot_fd_in.get())); Loading @@ -2587,18 +2573,20 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) while ((c = getopt(argc, argv, "dho:svqzpLPBRSV:w")) != -1) { switch (c) { // clang-format off case 'd': do_add_date = true; break; case 'z': do_zip_file = true; break; case 'o': out_dir = optarg; break; case 's': use_socket = true; break; case 'S': use_control_socket = true; break; case 's': stream_to_socket = true; break; case 'S': progress_updates_to_socket = true; break; case 'v': show_header_only = true; break; case 'q': do_vibrate = false; break; case 'p': do_screenshot = true; break; case 'P': do_progress_updates = true; break; case 'R': is_remote_mode = true; break; case 'L': limited_only = true; break; case 'V': break; // compatibility no-op case 'V': case 'd': case 'z': // compatibility no-op break; case 'w': // This was already processed break; Loading Loading @@ -2627,19 +2615,15 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } bool Dumpstate::DumpOptions::ValidateOptions() const { if (bugreport_fd.get() != -1 && !do_zip_file) { if (bugreport_fd.get() != -1 && stream_to_socket) { return false; } if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) { if ((progress_updates_to_socket || do_progress_updates) && stream_to_socket) { return false; } if (use_control_socket && !do_zip_file) { return false; } if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) { if (is_remote_mode && (do_progress_updates || stream_to_socket)) { return false; } return true; Loading Loading @@ -2714,11 +2698,9 @@ void Dumpstate::Cancel() { * The temporary bugreport is then populated via printfs, dumping contents of files and * output of commands to stdout. * * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final * text file. * A bunch of other files and dumps are added to the zip archive. * * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also * gets added to the archive. * The temporary bugreport file and the log file also get added to the archive. * * Bugreports are first generated in a local directory and later copied to the caller's fd * or directory if supplied. Loading Loading @@ -2766,14 +2748,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n", calling_uid, calling_package.c_str()); // Redirect output if needed bool is_redirecting = options_->OutputToFile(); // TODO: temporarily set progress until it's part of the Dumpstate constructor std::string stats_path = is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) : ""; android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()); progress_.reset(new Progress(stats_path)); if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) { Loading @@ -2796,24 +2773,23 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // If we are going to use a socket, do it as early as possible // to avoid timeouts from bugreport. if (options_->use_socket) { if (!redirect_to_socket(stdout, "dumpstate")) { return ERROR; } } if (options_->use_control_socket) { if (options_->stream_to_socket || options_->progress_updates_to_socket) { MYLOGD("Opening control socket\n"); control_socket_fd_ = open_socket("dumpstate"); control_socket_fd_ = open_socket_fn_("dumpstate"); if (control_socket_fd_ == -1) { return ERROR; } if (options_->progress_updates_to_socket) { options_->do_progress_updates = 1; } } if (is_redirecting) { PrepareToWriteToFile(); if (!PrepareToWriteToFile()) { return ERROR; } // Interactive, wear & telephony modes are default to true. // and may enable from cli option or when using control socket if (options_->do_progress_updates) { // clang-format off std::vector<std::string> am_args = { Loading @@ -2822,11 +2798,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // clang-format on // Send STARTED broadcast for apps that listen to bugreport generation events SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); if (options_->use_control_socket) { if (options_->progress_updates_to_socket) { dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str()); } } } /* read /proc/cmdline before dropping root */ FILE *cmdline = fopen("/proc/cmdline", "re"); Loading @@ -2839,7 +2814,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, Vibrate(150); } if (options_->do_zip_file && zip_file != nullptr) { if (zip_file != nullptr) { if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(), strerror(errno)); Loading @@ -2848,7 +2823,6 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, int dup_stdout_fd; int dup_stderr_fd; if (is_redirecting) { // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) { Loading @@ -2873,7 +2847,6 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", tmp_path_.c_str(), strerror(errno)); } } // Don't buffer stdout setvbuf(stdout, nullptr, _IONBF, 0); Loading Loading @@ -2926,14 +2899,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } /* close output if needed */ if (is_redirecting) { TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); } // Zip the (now complete) .tmp file within the internal directory. if (options_->OutputToFile()) { FinalizeFile(); } // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; Loading Loading @@ -2976,11 +2945,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, progress_->Save(); MYLOGI("done (id %d)\n", id_); if (is_redirecting) { TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); } if (options_->use_control_socket && control_socket_fd_ != -1) { if (control_socket_fd_ != -1) { MYLOGD("Closing control socket\n"); close(control_socket_fd_); } Loading Loading @@ -3051,10 +3018,7 @@ void Dumpstate::CleanupTmpFiles() { } void Dumpstate::EnableParallelRunIfNeeded() { // The thread pool needs to create temporary files to receive dump results. // That's why we only enable it when the bugreport client chooses to output // to a file. if (!PropertiesHelper::IsParallelRun() || !options_->OutputToFile()) { if (!PropertiesHelper::IsParallelRun()) { return; } dump_pool_ = std::make_unique<DumpPool>(bugreport_internal_dir_); Loading Loading @@ -3199,7 +3163,8 @@ Dumpstate::Dumpstate(const std::string& version) options_(new Dumpstate::DumpOptions()), last_reported_percent_progress_(0), version_(version), now_(time(nullptr)) { now_(time(nullptr)), open_socket_fn_(open_socket) { } Dumpstate& Dumpstate::GetInstance() { Loading Loading @@ -3833,7 +3798,7 @@ void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::stri RunCommand(title, dumpsys, options, false, out_fd); } int open_socket(const char *service) { static int open_socket(const char* service) { int s = android_get_control_socket(service); if (s < 0) { MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); Loading Loading @@ -3868,19 +3833,6 @@ int open_socket(const char *service) { return fd; } /* redirect output to a service control socket */ bool redirect_to_socket(FILE* redirect, const char* service) { int fd = open_socket(service); if (fd == -1) { return false; } fflush(redirect); // TODO: handle dup2 failure TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); return true; } // TODO: should call is_valid_output_file and/or be merged into it. void create_parent_dirs(const char *path) { char *chp = const_cast<char *> (path); Loading
cmds/dumpstate/dumpstate.h +12 −25 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ namespace dumpstate { class DumpstateTest; class ProgressTest; class ZippedBugReportStreamTest; } // namespace dumpstate } // namespace os Loading Loading @@ -197,6 +198,7 @@ struct DumpData { */ class Dumpstate { friend class android::os::dumpstate::DumpstateTest; friend class android::os::dumpstate::ZippedBugReportStreamTest; public: enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT }; Loading Loading @@ -388,12 +390,11 @@ class Dumpstate { * Structure to hold options that determine the behavior of dumpstate. */ struct DumpOptions { bool do_add_date = false; bool do_zip_file = false; bool do_vibrate = true; // Writes bugreport content to a socket; only flatfile format is supported. bool use_socket = false; bool use_control_socket = false; // Writes bugreport zipped file to a socket. bool stream_to_socket = false; // Writes generation progress updates to a socket. bool progress_updates_to_socket = false; bool do_screenshot = false; bool is_screenshot_copied = false; bool is_remote_mode = false; Loading Loading @@ -434,13 +435,6 @@ class Dumpstate { /* Returns true if the options set so far are consistent. */ bool ValidateOptions() const; /* Returns if options specified require writing bugreport to a file */ bool OutputToFile() const { // If we are not writing to socket, we will write to a file. If bugreport_fd is // specified, it is preferred. If not bugreport is written to /bugreports. return !use_socket; } /* Returns if options specified require writing to custom file location */ bool OutputToCustomFile() { // Custom location is only honored in limited mode. Loading @@ -466,7 +460,8 @@ class Dumpstate { std::unique_ptr<Progress> progress_; // When set, defines a socket file-descriptor use to report progress to bugreportz. // When set, defines a socket file-descriptor use to report progress to bugreportz // or to stream the zipped file to. int control_socket_fd_ = -1; // Bugreport format version; Loading @@ -478,8 +473,8 @@ class Dumpstate { // `bugreport-BUILD_ID`. std::string base_name_; // Name is the suffix part of the bugreport files - it's typically the date (when invoked with // `-d`), but it could be changed by the user.. // Name is the suffix part of the bugreport files - it's typically the date, // but it could be changed by the user.. std::string name_; std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; Loading Loading @@ -568,6 +563,8 @@ class Dumpstate { // called by Shell. RunStatus CopyBugreportIfUserConsented(int32_t calling_uid); std::function<int(const char *)> open_socket_fn_; // Used by GetInstance() only. explicit Dumpstate(const std::string& version = VERSION_CURRENT); Loading Loading @@ -601,16 +598,6 @@ int dump_file_from_fd(const char *title, const char *path, int fd); int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), int (*dump_from_fd)(const char* title, const char* path, int fd)); /** opens a socket and returns its file descriptor */ int open_socket(const char *service); /* * Redirects 'redirect' to a service control socket. * * Returns true if redirect succeeds. */ bool redirect_to_socket(FILE* redirect, const char* service); /* * Redirects 'redirect' to a file indicated by 'path', truncating it. * Loading