dumpstate.cpp revision 32af8c2aefd9a31e851c8f17168f19afcb5efb18
1/*
2 * Copyright (C) 2008 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#include <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <libgen.h>
21#include <limits.h>
22#include <memory>
23#include <regex>
24#include <set>
25#include <signal.h>
26#include <stdbool.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string>
30#include <string.h>
31#include <sys/prctl.h>
32#include <sys/resource.h>
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <sys/wait.h>
36#include <unistd.h>
37
38#include <android-base/file.h>
39#include <android-base/properties.h>
40#include <android-base/stringprintf.h>
41#include <android-base/unique_fd.h>
42#include <cutils/properties.h>
43#include <hardware_legacy/power.h>
44
45#include "private/android_filesystem_config.h"
46
47#define LOG_TAG "dumpstate"
48#include <cutils/log.h>
49
50#include "dumpstate.h"
51#include "ziparchive/zip_writer.h"
52
53#include <openssl/sha.h>
54
55using android::base::StringPrintf;
56
57/* read before root is shed */
58static char cmdline_buf[16384] = "(unknown)";
59static const char *dump_traces_path = NULL;
60
61// Command-line arguments as string
62static std::string args;
63
64// TODO: variables below should be part of dumpstate object
65static std::string buildType;
66static time_t now;
67static std::unique_ptr<ZipWriter> zip_writer;
68static std::set<std::string> mount_points;
69void add_mountinfo();
70/* suffix of the bugreport files - it's typically the date (when invoked with -d),
71 * although it could be changed by the user using a system property */
72static std::string suffix;
73static std::string extraOptions;
74
75#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
76#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
77
78#define RAFT_DIR "/data/misc/raft"
79#define RECOVERY_DIR "/cache/recovery"
80#define RECOVERY_DATA_DIR "/data/misc/recovery"
81#define LOGPERSIST_DATA_DIR "/data/misc/logd"
82#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
83#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
84#define TOMBSTONE_DIR "/data/tombstones"
85#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
86/* Can accomodate a tombstone number up to 9999. */
87#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
88#define NUM_TOMBSTONES  10
89#define WLUTIL "/vendor/xbin/wlutil"
90#define WAKE_LOCK_NAME "dumpstate_wakelock"
91
92typedef struct {
93  char name[TOMBSTONE_MAX_LEN];
94  int fd;
95} tombstone_data_t;
96
97static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
98
99// TODO: temporary variables and functions used during C++ refactoring
100static Dumpstate& ds = Dumpstate::GetInstance();
101static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
102                      const CommandOptions& options = CommandOptions::DEFAULT) {
103    return ds.RunCommand(title, fullCommand, options);
104}
105static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
106                       const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS,
107                       long dumpsysTimeout = 0) {
108    return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout);
109}
110static int DumpFile(const std::string& title, const std::string& path) {
111    return ds.DumpFile(title, path);
112}
113bool IsUserBuild() {
114    return ds.IsUserBuild();
115}
116
117/*
118 * List of supported zip format versions.
119 *
120 * See bugreport-format.md for more info.
121 */
122static std::string VERSION_DEFAULT = "1.0";
123
124// Relative directory (inside the zip) for all files copied as-is into the bugreport.
125static const std::string ZIP_ROOT_DIR = "FS";
126
127static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
128static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
129
130bool Dumpstate::IsUserBuild() {
131    return "user" == buildType;
132}
133
134bool Dumpstate::IsDryRun() {
135    return dryRun_;
136}
137
138/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
139 * otherwise, gets just those modified in the last half an hour. */
140static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
141    time_t thirty_minutes_ago = now - 60*30;
142    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
143        snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
144        int fd = TEMP_FAILURE_RETRY(open(data[i].name,
145                                         O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
146        struct stat st;
147        if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
148            (zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) {
149            data[i].fd = fd;
150        } else {
151            close(fd);
152            data[i].fd = -1;
153        }
154    }
155}
156
157// for_each_pid() callback to get mount info about a process.
158void do_mountinfo(int pid, const char *name) {
159    char path[PATH_MAX];
160
161    // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
162    // are added.
163    snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
164    char linkname[PATH_MAX];
165    ssize_t r = readlink(path, linkname, PATH_MAX);
166    if (r == -1) {
167        MYLOGE("Unable to read link for %s: %s\n", path, strerror(errno));
168        return;
169    }
170    linkname[r] = '\0';
171
172    if (mount_points.find(linkname) == mount_points.end()) {
173        // First time this mount point was found: add it
174        snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid);
175        if (add_zip_entry(ZIP_ROOT_DIR + path, path)) {
176            mount_points.insert(linkname);
177        } else {
178            MYLOGE("Unable to add mountinfo %s to zip file\n", path);
179        }
180    }
181}
182
183void add_mountinfo() {
184    if (!zip_writer) return;
185    std::string title = "MOUNT INFO";
186    mount_points.clear();
187    DurationReporter durationReporter(title, nullptr);
188    for_each_pid(do_mountinfo, nullptr);
189    MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size());
190}
191
192static void dump_dev_files(const char *title, const char *driverpath, const char *filename)
193{
194    DIR *d;
195    struct dirent *de;
196    char path[PATH_MAX];
197
198    d = opendir(driverpath);
199    if (d == NULL) {
200        return;
201    }
202
203    while ((de = readdir(d))) {
204        if (de->d_type != DT_LNK) {
205            continue;
206        }
207        snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename);
208        DumpFile(title, path);
209    }
210
211    closedir(d);
212}
213
214// return pid of a userspace process. If not found or error, return 0.
215static unsigned int pid_of_process(const char* ps_name) {
216    DIR *proc_dir;
217    struct dirent *ps;
218    unsigned int pid;
219    std::string cmdline;
220
221    if (!(proc_dir = opendir("/proc"))) {
222        MYLOGE("Can't open /proc\n");
223        return 0;
224    }
225
226    while ((ps = readdir(proc_dir))) {
227        if (!(pid = atoi(ps->d_name))) {
228            continue;
229        }
230        android::base::ReadFileToString("/proc/"
231                + std::string(ps->d_name) + "/cmdline", &cmdline);
232        if (cmdline.find(ps_name) == std::string::npos) {
233            continue;
234        } else {
235            closedir(proc_dir);
236            return pid;
237        }
238    }
239    closedir(proc_dir);
240    return 0;
241}
242
243// dump anrd's trace and add to the zip file.
244// 1. check if anrd is running on this device.
245// 2. send a SIGUSR1 to its pid which will dump anrd's trace.
246// 3. wait until the trace generation completes and add to the zip file.
247static bool dump_anrd_trace() {
248    unsigned int pid;
249    char buf[50], path[PATH_MAX];
250    struct dirent *trace;
251    struct stat st;
252    DIR *trace_dir;
253    int retry = 5;
254    long max_ctime = 0, old_mtime;
255    long long cur_size = 0;
256    const char *trace_path = "/data/misc/anrd/";
257
258    if (!zip_writer) {
259        MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
260        return false;
261    }
262
263    // find anrd's pid if it is running.
264    pid = pid_of_process("/system/xbin/anrd");
265
266    if (pid > 0) {
267        if (stat(trace_path, &st) == 0) {
268            old_mtime = st.st_mtime;
269        } else {
270            MYLOGE("Failed to find: %s\n", trace_path);
271            return false;
272        }
273
274        // send SIGUSR1 to the anrd to generate a trace.
275        sprintf(buf, "%u", pid);
276        if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf},
277                       CommandOptions::WithTimeout(1).Build())) {
278            MYLOGE("anrd signal timed out. Please manually collect trace\n");
279            return false;
280        }
281
282        while (retry-- > 0 && old_mtime == st.st_mtime) {
283            sleep(1);
284            stat(trace_path, &st);
285        }
286
287        if (retry < 0 && old_mtime == st.st_mtime) {
288            MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path);
289            return false;
290        }
291
292        // identify the trace file by its creation time.
293        if (!(trace_dir = opendir(trace_path))) {
294            MYLOGE("Can't open trace file under %s\n", trace_path);
295        }
296        while ((trace = readdir(trace_dir))) {
297            if (strcmp(trace->d_name, ".") == 0
298                    || strcmp(trace->d_name, "..") == 0) {
299                continue;
300            }
301            sprintf(path, "%s%s", trace_path, trace->d_name);
302            if (stat(path, &st) == 0) {
303                if (st.st_ctime > max_ctime) {
304                    max_ctime = st.st_ctime;
305                    sprintf(buf, "%s", trace->d_name);
306                }
307            }
308        }
309        closedir(trace_dir);
310
311        // Wait until the dump completes by checking the size of the trace.
312        if (max_ctime > 0) {
313            sprintf(path, "%s%s", trace_path, buf);
314            while(true) {
315                sleep(1);
316                if (stat(path, &st) == 0) {
317                    if (st.st_size == cur_size) {
318                        break;
319                    } else if (st.st_size > cur_size) {
320                        cur_size = st.st_size;
321                    } else {
322                        return false;
323                    }
324                } else {
325                    MYLOGE("Cant stat() %s anymore\n", path);
326                    return false;
327                }
328            }
329            // Add to the zip file.
330            if (!add_zip_entry("anrd_trace.txt", path)) {
331                MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
332            } else {
333                if (remove(path)) {
334                    MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno));
335                }
336                return true;
337            }
338        } else {
339            MYLOGE("Can't stats any trace file under %s\n", trace_path);
340        }
341    }
342    return false;
343}
344
345static void dump_systrace() {
346    if (!zip_writer) {
347        MYLOGD("Not dumping systrace because zip_writer is not set\n");
348        return;
349    }
350    std::string systrace_path = ds.bugreportDir_ + "/systrace-" + suffix + ".txt";
351    if (systrace_path.empty()) {
352        MYLOGE("Not dumping systrace because path is empty\n");
353        return;
354    }
355    const char* path = "/sys/kernel/debug/tracing/tracing_on";
356    long int is_tracing;
357    if (read_file_as_long(path, &is_tracing)) {
358        return; // error already logged
359    }
360    if (is_tracing <= 0) {
361        MYLOGD("Skipping systrace because '%s' content is '%ld'\n", path, is_tracing);
362        return;
363    }
364
365    MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
366            systrace_path.c_str());
367    if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path},
368                   CommandOptions::WithTimeout(120).Build())) {
369        MYLOGE("systrace timed out, its zip entry will be incomplete\n");
370        // TODO: RunCommand tries to kill the process, but atrace doesn't die
371        // peacefully; ideally, we should call strace to stop itself, but there is no such option
372        // yet (just a --async_stop, which stops and dump
373        // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) {
374        //   MYLOGE("could not stop systrace ");
375        // }
376    }
377    if (!add_zip_entry("systrace.txt", systrace_path)) {
378        MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
379    } else {
380        if (remove(systrace_path.c_str())) {
381            MYLOGE("Error removing systrace file %s: %s", systrace_path.c_str(), strerror(errno));
382        }
383    }
384}
385
386static void dump_raft() {
387    if (IsUserBuild()) {
388        return;
389    }
390
391    std::string raft_log_path = ds.bugreportDir_ + "/raft_log.txt";
392    if (raft_log_path.empty()) {
393        MYLOGD("raft_log_path is empty\n");
394        return;
395    }
396
397    struct stat s;
398    if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) {
399        MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR);
400        return;
401    }
402
403    CommandOptions options = CommandOptions::WithTimeout(600).Build();
404    if (!zip_writer) {
405        // Write compressed and encoded raft logs to stdout if not zip_writer.
406        RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
407        return;
408    }
409
410    RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_log_path}, options);
411    if (!add_zip_entry("raft_log.txt", raft_log_path)) {
412        MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
413    } else {
414        if (remove(raft_log_path.c_str())) {
415            MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
416        }
417    }
418}
419
420static bool skip_not_stat(const char *path) {
421    static const char stat[] = "/stat";
422    size_t len = strlen(path);
423    if (path[len - 1] == '/') { /* Directory? */
424        return false;
425    }
426    return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */
427}
428
429static bool skip_none(const char *path) {
430    return false;
431}
432
433static const char mmcblk0[] = "/sys/block/mmcblk0/";
434unsigned long worst_write_perf = 20000; /* in KB/s */
435
436//
437//  stat offsets
438// Name            units         description
439// ----            -----         -----------
440// read I/Os       requests      number of read I/Os processed
441#define __STAT_READ_IOS      0
442// read merges     requests      number of read I/Os merged with in-queue I/O
443#define __STAT_READ_MERGES   1
444// read sectors    sectors       number of sectors read
445#define __STAT_READ_SECTORS  2
446// read ticks      milliseconds  total wait time for read requests
447#define __STAT_READ_TICKS    3
448// write I/Os      requests      number of write I/Os processed
449#define __STAT_WRITE_IOS     4
450// write merges    requests      number of write I/Os merged with in-queue I/O
451#define __STAT_WRITE_MERGES  5
452// write sectors   sectors       number of sectors written
453#define __STAT_WRITE_SECTORS 6
454// write ticks     milliseconds  total wait time for write requests
455#define __STAT_WRITE_TICKS   7
456// in_flight       requests      number of I/Os currently in flight
457#define __STAT_IN_FLIGHT     8
458// io_ticks        milliseconds  total time this block device has been active
459#define __STAT_IO_TICKS      9
460// time_in_queue   milliseconds  total wait time for all requests
461#define __STAT_IN_QUEUE     10
462#define __STAT_NUMBER_FIELD 11
463//
464// read I/Os, write I/Os
465// =====================
466//
467// These values increment when an I/O request completes.
468//
469// read merges, write merges
470// =========================
471//
472// These values increment when an I/O request is merged with an
473// already-queued I/O request.
474//
475// read sectors, write sectors
476// ===========================
477//
478// These values count the number of sectors read from or written to this
479// block device.  The "sectors" in question are the standard UNIX 512-byte
480// sectors, not any device- or filesystem-specific block size.  The
481// counters are incremented when the I/O completes.
482#define SECTOR_SIZE 512
483//
484// read ticks, write ticks
485// =======================
486//
487// These values count the number of milliseconds that I/O requests have
488// waited on this block device.  If there are multiple I/O requests waiting,
489// these values will increase at a rate greater than 1000/second; for
490// example, if 60 read requests wait for an average of 30 ms, the read_ticks
491// field will increase by 60*30 = 1800.
492//
493// in_flight
494// =========
495//
496// This value counts the number of I/O requests that have been issued to
497// the device driver but have not yet completed.  It does not include I/O
498// requests that are in the queue but not yet issued to the device driver.
499//
500// io_ticks
501// ========
502//
503// This value counts the number of milliseconds during which the device has
504// had I/O requests queued.
505//
506// time_in_queue
507// =============
508//
509// This value counts the number of milliseconds that I/O requests have waited
510// on this block device.  If there are multiple I/O requests waiting, this
511// value will increase as the product of the number of milliseconds times the
512// number of requests waiting (see "read ticks" above for an example).
513#define S_TO_MS 1000
514//
515
516static int dump_stat_from_fd(const char *title __unused, const char *path, int fd) {
517    unsigned long long fields[__STAT_NUMBER_FIELD];
518    bool z;
519    char *cp, *buffer = NULL;
520    size_t i = 0;
521    FILE *fp = fdopen(fd, "rb");
522    getline(&buffer, &i, fp);
523    fclose(fp);
524    if (!buffer) {
525        return -errno;
526    }
527    i = strlen(buffer);
528    while ((i > 0) && (buffer[i - 1] == '\n')) {
529        buffer[--i] = '\0';
530    }
531    if (!*buffer) {
532        free(buffer);
533        return 0;
534    }
535    z = true;
536    for (cp = buffer, i = 0; i < (sizeof(fields) / sizeof(fields[0])); ++i) {
537        fields[i] = strtoull(cp, &cp, 10);
538        if (fields[i] != 0) {
539            z = false;
540        }
541    }
542    if (z) { /* never accessed */
543        free(buffer);
544        return 0;
545    }
546
547    if (!strncmp(path, mmcblk0, sizeof(mmcblk0) - 1)) {
548        path += sizeof(mmcblk0) - 1;
549    }
550
551    printf("%s: %s\n", path, buffer);
552    free(buffer);
553
554    if (fields[__STAT_IO_TICKS]) {
555        unsigned long read_perf = 0;
556        unsigned long read_ios = 0;
557        if (fields[__STAT_READ_TICKS]) {
558            unsigned long long divisor = fields[__STAT_READ_TICKS]
559                                       * fields[__STAT_IO_TICKS];
560            read_perf = ((unsigned long long)SECTOR_SIZE
561                           * fields[__STAT_READ_SECTORS]
562                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
563                                        / divisor;
564            read_ios = ((unsigned long long)S_TO_MS * fields[__STAT_READ_IOS]
565                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
566                                        / divisor;
567        }
568
569        unsigned long write_perf = 0;
570        unsigned long write_ios = 0;
571        if (fields[__STAT_WRITE_TICKS]) {
572            unsigned long long divisor = fields[__STAT_WRITE_TICKS]
573                                       * fields[__STAT_IO_TICKS];
574            write_perf = ((unsigned long long)SECTOR_SIZE
575                           * fields[__STAT_WRITE_SECTORS]
576                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
577                                        / divisor;
578            write_ios = ((unsigned long long)S_TO_MS * fields[__STAT_WRITE_IOS]
579                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
580                                        / divisor;
581        }
582
583        unsigned queue = (fields[__STAT_IN_QUEUE]
584                             + (fields[__STAT_IO_TICKS] >> 1))
585                                 / fields[__STAT_IO_TICKS];
586
587        if (!write_perf && !write_ios) {
588            printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n",
589                   path, read_perf, read_ios, queue);
590        } else {
591            printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n",
592                   path, read_perf, read_ios, write_perf, write_ios, queue);
593        }
594
595        /* bugreport timeout factor adjustment */
596        if ((write_perf > 1) && (write_perf < worst_write_perf)) {
597            worst_write_perf = write_perf;
598        }
599    }
600    return 0;
601}
602
603/* Copied policy from system/core/logd/LogBuffer.cpp */
604
605#define LOG_BUFFER_SIZE (256 * 1024)
606#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
607#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
608
609static bool valid_size(unsigned long value) {
610    if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
611        return false;
612    }
613
614    long pages = sysconf(_SC_PHYS_PAGES);
615    if (pages < 1) {
616        return true;
617    }
618
619    long pagesize = sysconf(_SC_PAGESIZE);
620    if (pagesize <= 1) {
621        pagesize = PAGE_SIZE;
622    }
623
624    // maximum memory impact a somewhat arbitrary ~3%
625    pages = (pages + 31) / 32;
626    unsigned long maximum = pages * pagesize;
627
628    if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
629        return true;
630    }
631
632    return value <= maximum;
633}
634
635// TODO: migrate to logd/LogBuffer.cpp or use android::base::GetProperty
636static unsigned long property_get_size(const char *key) {
637    unsigned long value;
638    char *cp, property[PROPERTY_VALUE_MAX];
639
640    property_get(key, property, "");
641    value = strtoul(property, &cp, 10);
642
643    switch(*cp) {
644    case 'm':
645    case 'M':
646        value *= 1024;
647    /* FALLTHRU */
648    case 'k':
649    case 'K':
650        value *= 1024;
651    /* FALLTHRU */
652    case '\0':
653        break;
654
655    default:
656        value = 0;
657    }
658
659    if (!valid_size(value)) {
660        value = 0;
661    }
662
663    return value;
664}
665
666/* timeout in ms */
667static unsigned long logcat_timeout(const char *name) {
668    static const char global_tuneable[] = "persist.logd.size"; // Settings App
669    static const char global_default[] = "ro.logd.size";       // BoardConfig.mk
670    char key[PROP_NAME_MAX];
671    unsigned long property_size, default_size;
672
673    default_size = property_get_size(global_tuneable);
674    if (!default_size) {
675        default_size = property_get_size(global_default);
676    }
677
678    snprintf(key, sizeof(key), "%s.%s", global_tuneable, name);
679    property_size = property_get_size(key);
680
681    if (!property_size) {
682        snprintf(key, sizeof(key), "%s.%s", global_default, name);
683        property_size = property_get_size(key);
684    }
685
686    if (!property_size) {
687        property_size = default_size;
688    }
689
690    if (!property_size) {
691        property_size = LOG_BUFFER_SIZE;
692    }
693
694    /* Engineering margin is ten-fold our guess */
695    return 10 * (property_size + worst_write_perf) / worst_write_perf;
696}
697
698/* End copy from system/core/logd/LogBuffer.cpp */
699
700/* dumps the current system state to stdout */
701void print_header(const std::string& version) {
702    std::string build, fingerprint, radio, bootloader, network;
703    char date[80];
704
705    build = android::base::GetProperty("ro.build.display.id", "(unknown)");
706    fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
707    buildType = android::base::GetProperty("ro.build.type", "(unknown)");
708    radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
709    bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
710    network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
711    strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
712
713    printf("========================================================\n");
714    printf("== dumpstate: %s\n", date);
715    printf("========================================================\n");
716
717    printf("\n");
718    printf("Build: %s\n", build.c_str());
719    // NOTE: fingerprint entry format is important for other tools.
720    printf("Build fingerprint: '%s'\n", fingerprint.c_str());
721    printf("Bootloader: %s\n", bootloader.c_str());
722    printf("Radio: %s\n", radio.c_str());
723    printf("Network: %s\n", network.c_str());
724
725    printf("Kernel: ");
726    DumpFile("", "/proc/version");
727    printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
728    printf("Bugreport format version: %s\n", version.c_str());
729    printf("Dumpstate info: id=%lu pid=%d dryRun=%d args=%s extraOptions=%s\n", ds.id_, getpid(),
730           ds.dryRun_, args.c_str(), extraOptions.c_str());
731    printf("\n");
732}
733
734// List of file extensions that can cause a zip file attachment to be rejected by some email
735// service providers.
736static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
737      ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp",
738      ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct",
739      ".shb", ".sys", ".vb",  ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
740};
741
742bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
743    if (!zip_writer) {
744        MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n",
745                entry_name.c_str());
746        return false;
747    }
748    std::string valid_name = entry_name;
749
750    // Rename extension if necessary.
751    size_t idx = entry_name.rfind(".");
752    if (idx != std::string::npos) {
753        std::string extension = entry_name.substr(idx);
754        std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
755        if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) {
756            valid_name = entry_name + ".renamed";
757            MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str());
758        }
759    }
760
761    // Logging statement  below is useful to time how long each entry takes, but it's too verbose.
762    // MYLOGD("Adding zip entry %s\n", entry_name.c_str());
763    int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(),
764            ZipWriter::kCompress, get_mtime(fd, now));
765    if (err) {
766        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
767                ZipWriter::ErrorCodeString(err));
768        return false;
769    }
770
771    std::vector<uint8_t> buffer(65536);
772    while (1) {
773        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
774        if (bytes_read == 0) {
775            break;
776        } else if (bytes_read == -1) {
777            MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
778            return false;
779        }
780        err = zip_writer->WriteBytes(buffer.data(), bytes_read);
781        if (err) {
782            MYLOGE("zip_writer->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
783            return false;
784        }
785    }
786
787    err = zip_writer->FinishEntry();
788    if (err) {
789        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
790        return false;
791    }
792
793    return true;
794}
795
796bool add_zip_entry(const std::string& entry_name, const std::string& entry_path) {
797    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK
798            | O_CLOEXEC)));
799    if (fd == -1) {
800        MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
801        return false;
802    }
803
804    return add_zip_entry_from_fd(entry_name, fd.get());
805}
806
807/* adds a file to the existing zipped bugreport */
808static int _add_file_from_fd(const char *title, const char *path, int fd) {
809    return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
810}
811
812// TODO: move to util.cpp
813void add_dir(const std::string& dir, bool recursive) {
814    if (!zip_writer) {
815        MYLOGD("Not adding dir %s because zip_writer is not set\n", dir.c_str());
816        return;
817    }
818    MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
819    DurationReporter durationReporter(dir, nullptr);
820    dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
821}
822
823/* adds a text entry entry to the existing zip file. */
824static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
825    if (!zip_writer) {
826        MYLOGD("Not adding text zip entry %s because zip_writer is not set\n", entry_name.c_str());
827        return false;
828    }
829    MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
830    int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, now);
831    if (err) {
832        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
833                ZipWriter::ErrorCodeString(err));
834        return false;
835    }
836
837    err = zip_writer->WriteBytes(content.c_str(), content.length());
838    if (err) {
839        MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
840                ZipWriter::ErrorCodeString(err));
841        return false;
842    }
843
844    err = zip_writer->FinishEntry();
845    if (err) {
846        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
847        return false;
848    }
849
850    return true;
851}
852
853static void dump_iptables() {
854    RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
855    RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
856    RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
857    /* no ip6 nat */
858    RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
859    RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
860    RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
861    RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
862}
863
864static void dumpstate(const std::string& screenshot_path, const std::string& version) {
865    DurationReporter durationReporter("DUMPSTATE");
866    unsigned long timeout;
867
868    dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
869    RunCommand("UPTIME", {"uptime"});
870    dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
871    dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
872    DumpFile("MEMORY INFO", "/proc/meminfo");
873    RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
874                            "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
875    RunCommand("PROCRANK", {"procrank"}, CommandOptions::AS_ROOT_20);
876    DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
877    DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
878    DumpFile("SLAB INFO", "/proc/slabinfo");
879    DumpFile("ZONEINFO", "/proc/zoneinfo");
880    DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo");
881    DumpFile("BUDDYINFO", "/proc/buddyinfo");
882    DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
883
884    DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
885    DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
886    DumpFile("KERNEL SYNC", "/d/sync");
887
888    RunCommand("PROCESSES AND THREADS",
889               {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
890    RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT_10);
891
892    RunCommand("PRINTENV", {"printenv"});
893    RunCommand("NETSTAT", {"netstat", "-n"});
894    struct stat s;
895    if (stat("/proc/modules", &s) != 0) {
896        MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
897    } else {
898        RunCommand("LSMOD", {"lsmod"});
899    }
900
901    do_dmesg();
902
903    RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT_10);
904    for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
905    for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
906    for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
907
908    /* Dump Bluetooth HCI logs */
909    add_dir("/data/misc/bluetooth/logs", true);
910
911    if (!screenshot_path.empty()) {
912        MYLOGI("taking late screenshot\n");
913        take_screenshot(screenshot_path);
914        MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
915    }
916
917    // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
918    // calculate timeout
919    timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
920    if (timeout < 20000) {
921        timeout = 20000;
922    }
923    RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
924               CommandOptions::WithTimeout(timeout / 1000).Build());
925    timeout = logcat_timeout("events");
926    if (timeout < 20000) {
927        timeout = 20000;
928    }
929    RunCommand("EVENT LOG",
930               {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
931               CommandOptions::WithTimeout(timeout / 1000).Build());
932    timeout = logcat_timeout("radio");
933    if (timeout < 20000) {
934        timeout = 20000;
935    }
936    RunCommand("RADIO LOG",
937               {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
938               CommandOptions::WithTimeout(timeout / 1000).Build());
939
940    RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
941
942    /* show the traces we collected in main(), if that was done */
943    if (dump_traces_path != NULL) {
944        DumpFile("VM TRACES JUST NOW", dump_traces_path);
945    }
946
947    /* only show ANR traces if they're less than 15 minutes old */
948    struct stat st;
949    std::string anrTracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
950    if (anrTracesPath.empty()) {
951        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
952    } else {
953        int fd = TEMP_FAILURE_RETRY(
954            open(anrTracesPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
955        if (fd < 0) {
956            printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anrTracesPath.c_str(), strerror(errno));
957      } else {
958          dump_file_from_fd("VM TRACES AT LAST ANR", anrTracesPath.c_str(), fd);
959      }
960    }
961
962    /* slow traces for slow operations */
963    if (!anrTracesPath.empty()) {
964        int tail = anrTracesPath.size() - 1;
965        while (tail > 0 && anrTracesPath.at(tail) != '/') {
966            tail--;
967        }
968        int i = 0;
969        while (1) {
970            anrTracesPath =
971                anrTracesPath.substr(0, tail + 1) + android::base::StringPrintf("slow%02d.txt", i);
972            if (stat(anrTracesPath.c_str(), &st)) {
973                // No traces file at this index, done with the files.
974                break;
975            }
976            DumpFile("VM TRACES WHEN SLOW", anrTracesPath.c_str());
977            i++;
978        }
979    }
980
981    int dumped = 0;
982    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
983        if (tombstone_data[i].fd != -1) {
984            const char *name = tombstone_data[i].name;
985            int fd = tombstone_data[i].fd;
986            dumped = 1;
987            if (zip_writer) {
988                if (!add_zip_entry_from_fd(ZIP_ROOT_DIR + name, fd)) {
989                    MYLOGE("Unable to add tombstone %s to zip file\n", name);
990                }
991            } else {
992                dump_file_from_fd("TOMBSTONE", name, fd);
993            }
994            close(fd);
995            tombstone_data[i].fd = -1;
996        }
997    }
998    if (!dumped) {
999        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
1000    }
1001
1002    DumpFile("NETWORK DEV INFO", "/proc/net/dev");
1003    DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
1004    DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
1005    DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
1006    DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
1007
1008    if (!stat(PSTORE_LAST_KMSG, &st)) {
1009        /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
1010        DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
1011    } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
1012        DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG);
1013    } else {
1014        /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
1015        DumpFile("LAST KMSG", "/proc/last_kmsg");
1016    }
1017
1018    /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
1019    RunCommand("LAST LOGCAT",
1020               {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-d", "*:v"});
1021
1022    /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
1023
1024    RunCommand("NETWORK INTERFACES", {"ip", "link"});
1025
1026    RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
1027    RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
1028
1029    RunCommand("IP RULES", {"ip", "rule", "show"});
1030    RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
1031
1032    dump_route_tables();
1033
1034    RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
1035    RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
1036    RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
1037    RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
1038               CommandOptions::WithTimeout(20).Build());
1039
1040#ifdef FWDUMP_bcmdhd
1041    RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT_5);
1042
1043    RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
1044
1045    RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
1046
1047#endif
1048    DumpFile("INTERRUPTS (1)", "/proc/interrupts");
1049
1050    RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
1051               CommandOptions::WithTimeout(10).Build());
1052
1053#ifdef FWDUMP_bcmdhd
1054    RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, CommandOptions::AS_ROOT_20);
1055
1056    RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
1057
1058    RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
1059#endif
1060    DumpFile("INTERRUPTS (2)", "/proc/interrupts");
1061
1062    print_properties();
1063
1064    RunCommand("VOLD DUMP", {"vdc", "dump"});
1065    RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
1066
1067    RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
1068
1069    RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
1070
1071    printf("------ BACKLIGHTS ------\n");
1072    printf("LCD brightness=");
1073    DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
1074    printf("Button brightness=");
1075    DumpFile("", "/sys/class/leds/button-backlight/brightness");
1076    printf("Keyboard brightness=");
1077    DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
1078    printf("ALS mode=");
1079    DumpFile("", "/sys/class/leds/lcd-backlight/als");
1080    printf("LCD driver registers:\n");
1081    DumpFile("", "/sys/class/leds/lcd-backlight/registers");
1082    printf("\n");
1083
1084    /* Binder state is expensive to look at as it uses a lot of memory. */
1085    DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
1086    DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
1087    DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
1088    DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
1089    DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
1090
1091    printf("========================================================\n");
1092    printf("== Board\n");
1093    printf("========================================================\n");
1094
1095    {
1096        DurationReporter tmpDr("dumpstate_board()");
1097        dumpstate_board();
1098        printf("\n");
1099    }
1100
1101    /* Migrate the ril_dumpstate to a dumpstate_board()? */
1102    int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
1103    if (rilDumpstateTimeout > 0) {
1104        // su does not exist on user builds, so try running without it.
1105        // This way any implementations of vril-dump that do not require
1106        // root can run on user builds.
1107        CommandOptions::CommandOptionsBuilder options =
1108            CommandOptions::WithTimeout(rilDumpstateTimeout);
1109        if (!IsUserBuild()) {
1110            options.AsRoot();
1111        }
1112        RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
1113    }
1114
1115    printf("========================================================\n");
1116    printf("== Android Framework Services\n");
1117    printf("========================================================\n");
1118
1119    RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(),
1120               10);
1121
1122    printf("========================================================\n");
1123    printf("== Checkins\n");
1124    printf("========================================================\n");
1125
1126    RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
1127    RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
1128    RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
1129    RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
1130    RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
1131    RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
1132
1133    printf("========================================================\n");
1134    printf("== Running Application Activities\n");
1135    printf("========================================================\n");
1136
1137    RunDumpsys("APP ACTIVITIES", {"activity", "all"});
1138
1139    printf("========================================================\n");
1140    printf("== Running Application Services\n");
1141    printf("========================================================\n");
1142
1143    RunDumpsys("APP SERVICES", {"activity", "service", "all"});
1144
1145    printf("========================================================\n");
1146    printf("== Running Application Providers\n");
1147    printf("========================================================\n");
1148
1149    RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
1150
1151    printf("========================================================\n");
1152    printf("== Final progress (pid %d): %d/%d (originally %d)\n", getpid(), ds.progress_,
1153           ds.weightTotal_, WEIGHT_TOTAL);
1154    printf("========================================================\n");
1155    printf("== dumpstate: done (id %lu)\n", ds.id_);
1156    printf("========================================================\n");
1157}
1158
1159static void ShowUsageAndExit(int exitCode = 1) {
1160    fprintf(stderr,
1161            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] "
1162            "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
1163            "  -h: display this help message\n"
1164            "  -b: play sound file instead of vibrate, at beginning of job\n"
1165            "  -e: play sound file instead of vibrate, at end of job\n"
1166            "  -o: write to file (instead of stdout)\n"
1167            "  -d: append date to filename (requires -o)\n"
1168            "  -p: capture screenshot to filename.png (requires -o)\n"
1169            "  -z: generate zipped file (requires -o)\n"
1170            "  -s: write output to control socket (for init)\n"
1171            "  -S: write file location to control socket (for init; requires -o and -z)"
1172            "  -q: disable vibrate\n"
1173            "  -B: send broadcast when finished (requires -o)\n"
1174            "  -P: send broadcast when started and update system properties on "
1175            "progress (requires -o and -B)\n"
1176            "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
1177            "shouldn't be used with -P)\n"
1178            "  -V: sets the bugreport format version (valid values: %s)\n",
1179            VERSION_DEFAULT.c_str());
1180    exit(exitCode);
1181}
1182
1183static void ExitOnInvalidArgs() {
1184    fprintf(stderr, "invalid combination of args\n");
1185    ShowUsageAndExit();
1186}
1187
1188static void wake_lock_releaser() {
1189    if (release_wake_lock(WAKE_LOCK_NAME) < 0) {
1190        MYLOGE("Failed to release wake lock: %s \n", strerror(errno));
1191    } else {
1192        MYLOGD("Wake lock released.\n");
1193    }
1194}
1195
1196static void sig_handler(int signo) {
1197    wake_lock_releaser();
1198    _exit(EXIT_FAILURE);
1199}
1200
1201static void register_sig_handler() {
1202    struct sigaction sa;
1203    sigemptyset(&sa.sa_mask);
1204    sa.sa_flags = 0;
1205    sa.sa_handler = sig_handler;
1206    sigaction(SIGPIPE, &sa, NULL); // broken pipe
1207    sigaction(SIGSEGV, &sa, NULL); // segment fault
1208    sigaction(SIGINT, &sa, NULL); // ctrl-c
1209    sigaction(SIGTERM, &sa, NULL); // killed
1210    sigaction(SIGQUIT, &sa, NULL); // quit
1211}
1212
1213/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
1214   temporary file.
1215 */
1216static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
1217                            const std::string& log_path, time_t now) {
1218    // Final timestamp
1219    char date[80];
1220    time_t the_real_now_please_stand_up = time(nullptr);
1221    strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
1222    MYLOGD("dumpstate id %lu finished around %s (%ld s)\n", ds.id_, date,
1223           the_real_now_please_stand_up - now);
1224
1225    if (!add_zip_entry(bugreport_name, bugreport_path)) {
1226        MYLOGE("Failed to add text entry to .zip file\n");
1227        return false;
1228    }
1229    if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
1230        MYLOGE("Failed to add main_entry.txt to .zip file\n");
1231        return false;
1232    }
1233
1234    // Add log file (which contains stderr output) to zip...
1235    fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
1236    if (!add_zip_entry("dumpstate_log.txt", log_path.c_str())) {
1237        MYLOGE("Failed to add dumpstate log to .zip file\n");
1238        return false;
1239    }
1240    // ... and re-opens it for further logging.
1241    redirect_to_existing_file(stderr, const_cast<char*>(log_path.c_str()));
1242    fprintf(stderr, "\n");
1243
1244    int32_t err = zip_writer->Finish();
1245    if (err) {
1246        MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
1247        return false;
1248    }
1249
1250    if (IsUserBuild()) {
1251        MYLOGD("Removing temporary file %s\n", bugreport_path.c_str())
1252        if (remove(bugreport_path.c_str())) {
1253            ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno));
1254        }
1255    } else {
1256        MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
1257    }
1258
1259    return true;
1260}
1261
1262static std::string SHA256_file_hash(std::string filepath) {
1263    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
1264            | O_CLOEXEC | O_NOFOLLOW)));
1265    if (fd == -1) {
1266        MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno));
1267        return NULL;
1268    }
1269
1270    SHA256_CTX ctx;
1271    SHA256_Init(&ctx);
1272
1273    std::vector<uint8_t> buffer(65536);
1274    while (1) {
1275        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd.get(), buffer.data(), buffer.size()));
1276        if (bytes_read == 0) {
1277            break;
1278        } else if (bytes_read == -1) {
1279            MYLOGE("read(%s): %s\n", filepath.c_str(), strerror(errno));
1280            return NULL;
1281        }
1282
1283        SHA256_Update(&ctx, buffer.data(), bytes_read);
1284    }
1285
1286    uint8_t hash[SHA256_DIGEST_LENGTH];
1287    SHA256_Final(hash, &ctx);
1288
1289    char hash_buffer[SHA256_DIGEST_LENGTH * 2 + 1];
1290    for(size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) {
1291        sprintf(hash_buffer + (i * 2), "%02x", hash[i]);
1292    }
1293    hash_buffer[sizeof(hash_buffer) - 1] = 0;
1294    return std::string(hash_buffer);
1295}
1296
1297int main(int argc, char *argv[]) {
1298    int do_add_date = 0;
1299    int do_zip_file = 0;
1300    int do_vibrate = 1;
1301    char* use_outfile = 0;
1302    int use_socket = 0;
1303    int use_control_socket = 0;
1304    int do_fb = 0;
1305    int do_broadcast = 0;
1306    int do_early_screenshot = 0;
1307    int is_remote_mode = 0;
1308    std::string version = VERSION_DEFAULT;
1309
1310    now = time(nullptr);
1311
1312    MYLOGI("begin\n");
1313
1314    if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
1315        MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
1316    } else {
1317        MYLOGD("Wake lock acquired.\n");
1318        atexit(wake_lock_releaser);
1319        register_sig_handler();
1320    }
1321
1322    ds.dryRun_ = android::base::GetBoolProperty("dumpstate.dry_run", false);
1323    if (ds.dryRun_) {
1324        MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
1325    }
1326
1327    // TODO: use helper function to convert argv into a string
1328    for (int i = 0; i < argc; i++) {
1329        args += argv[i];
1330        if (i < argc - 1) {
1331            args += " ";
1332        }
1333    }
1334
1335    extraOptions = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
1336    MYLOGI("Dumpstate args: %s (extra options: %s)\n", args.c_str(), extraOptions.c_str());
1337
1338    /* gets the sequential id */
1339    int lastId = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
1340    ds.id_ = ++lastId;
1341    android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(lastId));
1342    MYLOGI("dumpstate id: %lu\n", ds.id_);
1343
1344    /* set as high priority, and protect from OOM killer */
1345    setpriority(PRIO_PROCESS, 0, -20);
1346
1347    FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
1348    if (oom_adj) {
1349        fputs("-1000", oom_adj);
1350        fclose(oom_adj);
1351    } else {
1352        /* fallback to kernels <= 2.6.35 */
1353        oom_adj = fopen("/proc/self/oom_adj", "we");
1354        if (oom_adj) {
1355            fputs("-17", oom_adj);
1356            fclose(oom_adj);
1357        }
1358    }
1359
1360    /* parse arguments */
1361    int c;
1362    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
1363        switch (c) {
1364            // clang-format off
1365            case 'd': do_add_date = 1;          break;
1366            case 'z': do_zip_file = 1;          break;
1367            case 'o': use_outfile = optarg;     break;
1368            case 's': use_socket = 1;           break;
1369            case 'S': use_control_socket = 1;   break;
1370            case 'v': break;  // compatibility no-op
1371            case 'q': do_vibrate = 0;           break;
1372            case 'p': do_fb = 1;                break;
1373            case 'P': ds.updateProgress_ = 1;   break;
1374            case 'R': is_remote_mode = 1;       break;
1375            case 'B': do_broadcast = 1;         break;
1376            case 'V': version = optarg;         break;
1377            case 'h':
1378                ShowUsageAndExit(0);
1379                break;
1380            default:
1381                fprintf(stderr, "Invalid option: %c\n", c);
1382                ShowUsageAndExit();
1383                // clang-format on
1384        }
1385    }
1386
1387    if (!extraOptions.empty()) {
1388        // Framework uses a system property to override some command-line args.
1389        // Currently, it contains the type of the requested bugreport.
1390        if (extraOptions == "bugreportplus") {
1391            MYLOGD("Running as bugreportplus: add -P, remove -p\n");
1392            ds.updateProgress_ = 1;
1393            do_fb = 0;
1394        } else if (extraOptions == "bugreportremote") {
1395            MYLOGD("Running as bugreportremote: add -q -R, remove -p\n");
1396            do_vibrate = 0;
1397            is_remote_mode = 1;
1398            do_fb = 0;
1399        } else if (extraOptions == "bugreportwear") {
1400            MYLOGD("Running as bugreportwear: add -P\n");
1401            ds.updateProgress_ = 1;
1402        } else {
1403            MYLOGE("Unknown extra option: %s\n", extraOptions.c_str());
1404        }
1405        // Reset the property
1406        android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
1407    }
1408
1409    if ((do_zip_file || do_add_date || ds.updateProgress_ || do_broadcast) && !use_outfile) {
1410        ExitOnInvalidArgs();
1411    }
1412
1413    if (use_control_socket && !do_zip_file) {
1414        ExitOnInvalidArgs();
1415    }
1416
1417    if (ds.updateProgress_ && !do_broadcast) {
1418        ExitOnInvalidArgs();
1419    }
1420
1421    if (is_remote_mode && (ds.updateProgress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
1422        ExitOnInvalidArgs();
1423    }
1424
1425    if (version != VERSION_DEFAULT) {
1426        ShowUsageAndExit();
1427    }
1428
1429    MYLOGI("bugreport format version: %s\n", version.c_str());
1430
1431    do_early_screenshot = ds.updateProgress_;
1432
1433    // If we are going to use a socket, do it as early as possible
1434    // to avoid timeouts from bugreport.
1435    if (use_socket) {
1436        redirect_to_socket(stdout, "dumpstate");
1437    }
1438
1439    if (use_control_socket) {
1440        MYLOGD("Opening control socket\n");
1441        ds.controlSocketFd_ = open_socket("dumpstate");
1442        ds.updateProgress_ = 1;
1443    }
1444
1445    /* full path of the temporary file containing the bugreport */
1446    std::string tmp_path;
1447
1448    /* full path of the file containing the dumpstate logs */
1449    std::string log_path;
1450
1451    /* full path of the systrace file, when enabled */
1452    std::string systrace_path;
1453
1454    /* full path of the temporary file containing the screenshot (when requested) */
1455    std::string screenshot_path;
1456
1457    /* base name (without suffix or extensions) of the bugreport files */
1458    std::string base_name;
1459
1460    /* pointer to the actual path, be it zip or text */
1461    std::string path;
1462
1463    /* pointer to the zipped file */
1464    std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose);
1465
1466    /* redirect output if needed */
1467    bool is_redirecting = !use_socket && use_outfile;
1468
1469    if (is_redirecting) {
1470        ds.bugreportDir_ = dirname(use_outfile);
1471        base_name = basename(use_outfile);
1472        if (do_add_date) {
1473            char date[80];
1474            strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&now));
1475            suffix = date;
1476        } else {
1477            suffix = "undated";
1478        }
1479        std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
1480        base_name = base_name + "-" + buildId;
1481        if (do_fb) {
1482            // TODO: if dumpstate was an object, the paths could be internal variables and then
1483            // we could have a function to calculate the derived values, such as:
1484            //     screenshot_path = GetPath(".png");
1485            screenshot_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".png";
1486        }
1487        tmp_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".tmp";
1488        log_path =
1489            ds.bugreportDir_ + "/dumpstate_log-" + suffix + "-" + std::to_string(getpid()) + ".txt";
1490
1491        MYLOGD(
1492            "Bugreport dir: %s\n"
1493            "Base name: %s\n"
1494            "Suffix: %s\n"
1495            "Log path: %s\n"
1496            "Temporary path: %s\n"
1497            "Screenshot path: %s\n",
1498            ds.bugreportDir_.c_str(), base_name.c_str(), suffix.c_str(), log_path.c_str(),
1499            tmp_path.c_str(), screenshot_path.c_str());
1500
1501        if (do_zip_file) {
1502            path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".zip";
1503            MYLOGD("Creating initial .zip file (%s)\n", path.c_str());
1504            create_parent_dirs(path.c_str());
1505            zip_file.reset(fopen(path.c_str(), "wb"));
1506            if (!zip_file) {
1507                MYLOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
1508                do_zip_file = 0;
1509            } else {
1510                zip_writer.reset(new ZipWriter(zip_file.get()));
1511            }
1512            add_text_zip_entry("version.txt", version);
1513        }
1514
1515        if (ds.updateProgress_) {
1516            if (do_broadcast) {
1517                // clang-format off
1518                std::vector<std::string> am_args = {
1519                     "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
1520                     "--es", "android.intent.extra.NAME", suffix,
1521                     "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
1522                     "--ei", "android.intent.extra.PID", std::to_string(getpid()),
1523                     "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
1524                };
1525                // clang-format on
1526                send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
1527            }
1528            if (use_control_socket) {
1529                dprintf(ds.controlSocketFd_, "BEGIN:%s\n", path.c_str());
1530            }
1531        }
1532    }
1533
1534    /* read /proc/cmdline before dropping root */
1535    FILE *cmdline = fopen("/proc/cmdline", "re");
1536    if (cmdline) {
1537        fgets(cmdline_buf, sizeof(cmdline_buf), cmdline);
1538        fclose(cmdline);
1539    }
1540
1541    /* open the vibrator before dropping root */
1542    std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
1543    if (do_vibrate) {
1544        vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we"));
1545        if (vibrator) {
1546            vibrate(vibrator.get(), 150);
1547        }
1548    }
1549
1550    if (do_fb && do_early_screenshot) {
1551        if (screenshot_path.empty()) {
1552            // should not have happened
1553            MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
1554        } else {
1555            MYLOGI("taking early screenshot\n");
1556            take_screenshot(screenshot_path);
1557            MYLOGI("wrote screenshot: %s\n", screenshot_path.c_str());
1558            if (chown(screenshot_path.c_str(), AID_SHELL, AID_SHELL)) {
1559                MYLOGE("Unable to change ownership of screenshot file %s: %s\n",
1560                        screenshot_path.c_str(), strerror(errno));
1561            }
1562        }
1563    }
1564
1565    if (do_zip_file) {
1566        if (chown(path.c_str(), AID_SHELL, AID_SHELL)) {
1567            MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno));
1568        }
1569    }
1570
1571    if (is_redirecting) {
1572        redirect_to_file(stderr, const_cast<char*>(log_path.c_str()));
1573        if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) {
1574            MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
1575                    log_path.c_str(), strerror(errno));
1576        }
1577        /* TODO: rather than generating a text file now and zipping it later,
1578           it would be more efficient to redirect stdout to the zip entry
1579           directly, but the libziparchive doesn't support that option yet. */
1580        redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
1581        if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) {
1582            MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
1583                    tmp_path.c_str(), strerror(errno));
1584        }
1585    }
1586    // NOTE: there should be no stdout output until now, otherwise it would break the header.
1587    // In particular, DurationReport objects should be created passing 'title, NULL', so their
1588    // duration is logged into MYLOG instead.
1589    print_header(version);
1590
1591    // Dumps systrace right away, otherwise it will be filled with unnecessary events.
1592    // First try to dump anrd trace if the daemon is running. Otherwise, dump
1593    // the raw trace.
1594    if (!dump_anrd_trace()) {
1595        dump_systrace();
1596    }
1597
1598    // Invoking the following dumpsys calls before dump_traces() to try and
1599    // keep the system stats as close to its initial state as possible.
1600    RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
1601               CommandOptions::WithTimeout(90).DropRoot().Build());
1602    RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
1603               CommandOptions::WithTimeout(10).DropRoot().Build());
1604
1605    // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
1606    dump_raft();
1607
1608    /* collect stack traces from Dalvik and native processes (needs root) */
1609    dump_traces_path = dump_traces();
1610
1611    /* Run some operations that require root. */
1612    get_tombstone_fds(tombstone_data);
1613    add_dir(RECOVERY_DIR, true);
1614    add_dir(RECOVERY_DATA_DIR, true);
1615    add_dir(LOGPERSIST_DATA_DIR, false);
1616    if (!IsUserBuild()) {
1617        add_dir(PROFILE_DATA_DIR_CUR, true);
1618        add_dir(PROFILE_DATA_DIR_REF, true);
1619    }
1620    add_mountinfo();
1621    dump_iptables();
1622
1623    // Capture any IPSec policies in play.  No keys are exposed here.
1624    RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
1625               CommandOptions::WithTimeout(10).Build());
1626
1627    // Run ss as root so we can see socket marks.
1628    RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build());
1629
1630    if (!drop_root_user()) {
1631        return -1;
1632    }
1633
1634    dumpstate(do_early_screenshot ? "": screenshot_path, version);
1635
1636    /* close output if needed */
1637    if (is_redirecting) {
1638        fclose(stdout);
1639    }
1640
1641    /* rename or zip the (now complete) .tmp file to its final location */
1642    if (use_outfile) {
1643
1644        /* check if user changed the suffix using system properties */
1645        std::string name = android::base::GetProperty(
1646            android::base::StringPrintf("dumpstate.%d.name", getpid()), "");
1647        bool change_suffix= false;
1648        if (!name.empty()) {
1649            /* must whitelist which characters are allowed, otherwise it could cross directories */
1650            std::regex valid_regex("^[-_a-zA-Z0-9]+$");
1651            if (std::regex_match(name.c_str(), valid_regex)) {
1652                change_suffix = true;
1653            } else {
1654                MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
1655            }
1656        }
1657        if (change_suffix) {
1658            MYLOGI("changing suffix from %s to %s\n", suffix.c_str(), name.c_str());
1659            suffix = name;
1660            if (!screenshot_path.empty()) {
1661                std::string new_screenshot_path =
1662                    ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".png";
1663                if (rename(screenshot_path.c_str(), new_screenshot_path.c_str())) {
1664                    MYLOGE("rename(%s, %s): %s\n", screenshot_path.c_str(),
1665                            new_screenshot_path.c_str(), strerror(errno));
1666                } else {
1667                    screenshot_path = new_screenshot_path;
1668                }
1669            }
1670        }
1671
1672        bool do_text_file = true;
1673        if (do_zip_file) {
1674            std::string entry_name = base_name + "-" + suffix + ".txt";
1675            MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
1676            if (!finish_zip_file(entry_name, tmp_path, log_path, now)) {
1677                MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
1678                do_text_file = true;
1679            } else {
1680                do_text_file = false;
1681                // Since zip file is already created, it needs to be renamed.
1682                std::string new_path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".zip";
1683                if (path != new_path) {
1684                    MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str());
1685                    if (rename(path.c_str(), new_path.c_str())) {
1686                        MYLOGE("rename(%s, %s): %s\n", path.c_str(),
1687                                new_path.c_str(), strerror(errno));
1688                    } else {
1689                        path = new_path;
1690                    }
1691                }
1692            }
1693        }
1694        if (do_text_file) {
1695            path = ds.bugreportDir_ + "/" + base_name + "-" + suffix + ".txt";
1696            MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
1697            if (rename(tmp_path.c_str(), path.c_str())) {
1698                MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
1699                path.clear();
1700            }
1701        }
1702        if (use_control_socket) {
1703            if (do_text_file) {
1704                dprintf(ds.controlSocketFd_,
1705                        "FAIL:could not create zip file, check %s "
1706                        "for more details\n",
1707                        log_path.c_str());
1708            } else {
1709                dprintf(ds.controlSocketFd_, "OK:%s\n", path.c_str());
1710            }
1711        }
1712    }
1713
1714    /* vibrate a few but shortly times to let user know it's finished */
1715    if (vibrator) {
1716        for (int i = 0; i < 3; i++) {
1717            vibrate(vibrator.get(), 75);
1718            usleep((75 + 50) * 1000);
1719        }
1720    }
1721
1722    /* tell activity manager we're done */
1723    if (do_broadcast) {
1724        if (!path.empty()) {
1725            MYLOGI("Final bugreport path: %s\n", path.c_str());
1726            // clang-format off
1727            std::vector<std::string> am_args = {
1728                 "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
1729                 "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
1730                 "--ei", "android.intent.extra.PID", std::to_string(getpid()),
1731                 "--ei", "android.intent.extra.MAX", std::to_string(ds.weightTotal_),
1732                 "--es", "android.intent.extra.BUGREPORT", path,
1733                 "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
1734            };
1735            // clang-format on
1736            if (do_fb) {
1737                am_args.push_back("--es");
1738                am_args.push_back("android.intent.extra.SCREENSHOT");
1739                am_args.push_back(screenshot_path);
1740            }
1741            if (is_remote_mode) {
1742                am_args.push_back("--es");
1743                am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
1744                am_args.push_back(SHA256_file_hash(path));
1745                send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
1746            } else {
1747                send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
1748            }
1749        } else {
1750            MYLOGE("Skipping finished broadcast because bugreport could not be generated\n");
1751        }
1752    }
1753
1754    MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weightTotal_, WEIGHT_TOTAL);
1755    MYLOGI("done (id %lu)\n", ds.id_);
1756
1757    if (is_redirecting) {
1758        fclose(stderr);
1759    }
1760
1761    if (use_control_socket && ds.controlSocketFd_ != -1) {
1762        MYLOGD("Closing control socket\n");
1763        close(ds.controlSocketFd_);
1764    }
1765
1766    return 0;
1767}
1768