allow dumpstate to work with dmesg_restrict=1

Ensure that dumpstate has the CAP_SYSLOG permission, so that
it can always read the kernel syslog buffer. This is needed
to ensure that "adb bugreport" shows the dmesg log buffer.

Bug: 5585365
Change-Id: I4b561b77ada83dc875de80fe3ed15743c5958d09
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 399e668..d5d65c1 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -25,6 +25,8 @@
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <linux/capability.h>
+#include <linux/prctl.h>
 
 #include <cutils/properties.h>
 
@@ -169,7 +171,7 @@
 
     print_properties();
 
-    run_command("KERNEL LOG", 20, "dmesg", NULL);
+    do_dmesg();
 
     dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
     dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
@@ -192,6 +194,7 @@
     dump_file("PACKAGE SETTINGS", "/data/system/packages.xml");
     dump_file("PACKAGE UID ERRORS", "/data/system/uiderrors.txt");
 
+    /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
     dump_file("LAST KMSG", "/proc/last_kmsg");
     run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
     dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
@@ -315,6 +318,11 @@
     }
 
     if (getuid() == 0) {
+        if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+            LOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+            return -1;
+        }
+
         /* switch to non-root user and group */
         gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT, AID_INET };
         if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
@@ -329,6 +337,23 @@
             LOGE("Unable to setuid, aborting: %s\n", strerror(errno));
             return -1;
         }
+
+        struct __user_cap_header_struct capheader;
+        struct __user_cap_data_struct capdata[2];
+        memset(&capheader, 0, sizeof(capheader));
+        memset(&capdata, 0, sizeof(capdata));
+        capheader.version = _LINUX_CAPABILITY_VERSION_3;
+        capheader.pid = 0;
+
+        capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+        capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+        capdata[0].inheritable = 0;
+        capdata[1].inheritable = 0;
+
+        if (capset(&capheader, &capdata[0]) < 0) {
+            LOGE("capset failed: %s\n", strerror(errno));
+            return -1;
+        }
     }
 
     char path[PATH_MAX], tmp_path[PATH_MAX];
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 6d66b1b..b02db0b 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -48,6 +48,9 @@
 /* Runs "showmap" for a process */
 void do_showmap(int pid, const char *name);
 
+/* Gets the dmesg output for the kernel */
+void do_dmesg();
+
 /* Play a sound via Stagefright */
 void play_sound(const char* path);
 
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 14984ec..21526f9 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -28,6 +28,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <sys/klog.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -96,6 +97,30 @@
     return;
 }
 
+void do_dmesg() {
+    printf("------ KERNEL LOG (dmesg) ------\n");
+    int size = klogctl(10, NULL, 0); /* Get size of kernel buffer */
+    if (size <= 0) {
+        printf("Unexpected klogctl return value: %d\n\n", size);
+        return;
+    }
+    char *buf = (char *) malloc(size + 1);
+    if (buf == NULL) {
+        printf("memory allocation failed\n\n");
+        return;
+    }
+    int retval = klogctl(KLOG_READ_ALL, buf, size);
+    if (retval < 0) {
+        printf("klogctl failure\n\n");
+        free(buf);
+        return;
+    }
+    buf[retval] = '\0';
+    printf("%s\n\n", buf);
+    free(buf);
+    return;
+}
+
 void do_showmap(int pid, const char *name) {
     char title[255];
     char arg[255];