Make init handle reboots

Move the responsibility for rebooting the system from the
reboot command to init. Init is in a better position to take
actions to bring the system down cleanly, including making sure
filesystems are mounted read-only.

The only UIDs which can perform an init triggered reboot are
root, system, and shell.

Modify the reboot command so that it calls into init to perform
the reboot. The reboot command no longer requires CAP_SYS_BOOT.

Remove the -n reboot option and code which supports it.  Anyone needing
to do an unclean shutdown can just do a 'echo c > /proc/sysrq-trigger'.

Modify adb so that it calls into init to perform a shutdown.

Bug: 8646621
Change-Id: I84c0513acb549720cb0e8c9fcbda0050f5c396f5
diff --git a/adb/adb.c b/adb/adb.c
index 9d2b86e..6580c6e 100644
--- a/adb/adb.c
+++ b/adb/adb.c
@@ -1199,9 +1199,8 @@
 #endif
     int i;
     for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
-        if (i == CAP_SETUID || i == CAP_SETGID || i == CAP_SYS_BOOT) {
+        if (i == CAP_SETUID || i == CAP_SETGID) {
             // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
-            // CAP_SYS_BOOT          needed by /system/bin/reboot
             continue;
         }
         int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
@@ -1302,13 +1301,6 @@
     /* don't listen on a port (default 5037) if running in secure mode */
     /* don't run as root if we are running in secure mode */
     if (should_drop_privileges()) {
-        struct __user_cap_header_struct header;
-        struct __user_cap_data_struct cap[2];
-
-        if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
-            exit(1);
-        }
-
         drop_capabilities_bounding_set_if_needed();
 
         /* add extra groups:
@@ -1338,16 +1330,6 @@
             exit(1);
         }
 
-        memset(&header, 0, sizeof(header));
-        memset(cap, 0, sizeof(cap));
-
-        /* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */
-        header.version = _LINUX_CAPABILITY_VERSION_3;
-        header.pid = 0;
-        cap[CAP_TO_INDEX(CAP_SYS_BOOT)].effective |= CAP_TO_MASK(CAP_SYS_BOOT);
-        cap[CAP_TO_INDEX(CAP_SYS_BOOT)].permitted |= CAP_TO_MASK(CAP_SYS_BOOT);
-        capset(&header, cap);
-
         D("Local port disabled\n");
     } else {
         char local_name[30];
diff --git a/adb/services.c b/adb/services.c
index 54d21a8..e82a0ea 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -165,6 +165,7 @@
 void reboot_service(int fd, void *arg)
 {
     char buf[100];
+    char property_val[PROPERTY_VALUE_MAX];
     int pid, ret;
 
     sync();
@@ -182,11 +183,19 @@
         waitpid(pid, &ret, 0);
     }
 
-    ret = android_reboot(ANDROID_RB_RESTART2, 0, (char *) arg);
+    ret = snprintf(property_val, sizeof(property_val), "reboot,%s", (char *) arg);
+    if (ret >= (int) sizeof(property_val)) {
+        snprintf(buf, sizeof(buf), "reboot string too long. length=%d\n", ret);
+        writex(fd, buf, strlen(buf));
+        goto cleanup;
+    }
+
+    ret = property_set(ANDROID_RB_PROPERTY, property_val);
     if (ret < 0) {
-        snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno));
+        snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret);
         writex(fd, buf, strlen(buf));
     }
+cleanup:
     free(arg);
     adb_close(fd);
 }
diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h
index 0c79be7..8c30e8e 100644
--- a/include/cutils/android_reboot.h
+++ b/include/cutils/android_reboot.h
@@ -24,9 +24,8 @@
 #define ANDROID_RB_POWEROFF 0xDEAD0002
 #define ANDROID_RB_RESTART2 0xDEAD0003
 
-/* Flags */
-#define ANDROID_RB_FLAG_NO_SYNC       0x1
-#define ANDROID_RB_FLAG_NO_REMOUNT_RO 0x2
+/* Properties */
+#define ANDROID_RB_PROPERTY "sys.powerctl"
 
 int android_reboot(int cmd, int flags, char *arg);
 
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 850e0bd..d69b332 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -230,7 +230,6 @@
 
     /* the following files have enhanced capabilities and ARE included in user builds. */
     { 00750, AID_ROOT,      AID_SHELL,     (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" },
-    { 00750, AID_ROOT,      AID_SHELL,     1 << CAP_SYS_BOOT, "system/bin/reboot" },
 
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
diff --git a/init/builtins.c b/init/builtins.c
index 0f9f131..9ae9ba3 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -32,6 +32,7 @@
 #include <sys/wait.h>
 #include <linux/loop.h>
 #include <cutils/partition_utils.h>
+#include <cutils/android_reboot.h>
 #include <sys/system_properties.h>
 #include <fs_mgr.h>
 
@@ -599,6 +600,43 @@
     return 0;
 }
 
+int do_powerctl(int nargs, char **args)
+{
+    char command[PROP_VALUE_MAX];
+    int res;
+    int len = 0;
+    int cmd = 0;
+    char *reboot_target;
+
+    res = expand_props(command, args[1], sizeof(command));
+    if (res) {
+        ERROR("powerctl: cannot expand '%s'\n", args[1]);
+        return -EINVAL;
+    }
+
+    if (strncmp(command, "shutdown", 8) == 0) {
+        cmd = ANDROID_RB_POWEROFF;
+        len = 8;
+    } else if (strncmp(command, "reboot", 6) == 0) {
+        cmd = ANDROID_RB_RESTART2;
+        len = 6;
+    } else {
+        ERROR("powerctl: unrecognized command '%s'\n", command);
+        return -EINVAL;
+    }
+
+    if (command[len] == ',') {
+        reboot_target = &command[len + 1];
+    } else if (command[len] == '\0') {
+        reboot_target = "";
+    } else {
+        ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
+        return -EINVAL;
+    }
+
+    return android_reboot(cmd, 0, reboot_target);
+}
+
 int do_trigger(int nargs, char **args)
 {
     action_for_each_trigger(args[1], action_add_queue_tail);
diff --git a/init/init_parser.c b/init/init_parser.c
index 686640e..a1d2423 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -130,6 +130,8 @@
         if (!strcmp(s, "neshot")) return K_oneshot;
         if (!strcmp(s, "nrestart")) return K_onrestart;
         break;
+    case 'p':
+        if (!strcmp(s, "owerctl")) return K_powerctl;
     case 'r':
         if (!strcmp(s, "estart")) return K_restart;
         if (!strcmp(s, "estorecon")) return K_restorecon;
diff --git a/init/keywords.h b/init/keywords.h
index f188db5..f147506 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -14,6 +14,7 @@
 int do_mkdir(int nargs, char **args);
 int do_mount_all(int nargs, char **args);
 int do_mount(int nargs, char **args);
+int do_powerctl(int nargs, char **args);
 int do_restart(int nargs, char **args);
 int do_restorecon(int nargs, char **args);
 int do_rm(int nargs, char **args);
@@ -66,6 +67,7 @@
     KEYWORD(on,          SECTION, 0, 0)
     KEYWORD(oneshot,     OPTION,  0, 0)
     KEYWORD(onrestart,   OPTION,  0, 0)
+    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
     KEYWORD(restart,     COMMAND, 1, do_restart)
     KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
     KEYWORD(rm,          COMMAND, 1, do_rm)
diff --git a/init/property_service.c b/init/property_service.c
old mode 100755
new mode 100644
index 5780001..6bf06b4
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -77,6 +77,7 @@
     { "runtime.",         AID_SYSTEM,   0 },
     { "hw.",              AID_SYSTEM,   0 },
     { "sys.",             AID_SYSTEM,   0 },
+    { "sys.powerctl",     AID_SHELL,    0 },
     { "service.",         AID_SYSTEM,   0 },
     { "wlan.",            AID_SYSTEM,   0 },
     { "bluetooth.",       AID_BLUETOOTH,   0 },
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index 33a7358..16f82bb 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -105,11 +105,8 @@
 {
     int ret;
 
-    if (!(flags & ANDROID_RB_FLAG_NO_SYNC))
-        sync();
-
-    if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))
-        remount_ro();
+    sync();
+    remount_ro();
 
     switch (cmd) {
         case ANDROID_RB_RESTART:
diff --git a/reboot/reboot.c b/reboot/reboot.c
index 45d8a8e..0e5170d 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -17,35 +17,34 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <cutils/properties.h>
 #include <cutils/android_reboot.h>
 #include <unistd.h>
 
 int main(int argc, char *argv[])
 {
     int ret;
-    int nosync = 0;
-    int poweroff = 0;
-    int flags = 0;
+    size_t prop_len;
+    char property_val[PROPERTY_VALUE_MAX];
+    const char *cmd = "reboot";
+    char *optarg = "";
 
     opterr = 0;
     do {
         int c;
 
-        c = getopt(argc, argv, "np");
+        c = getopt(argc, argv, "p");
 
         if (c == EOF) {
             break;
         }
 
         switch (c) {
-        case 'n':
-            nosync = 1;
-            break;
         case 'p':
-            poweroff = 1;
+            cmd = "shutdown";
             break;
         case '?':
-            fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
+            fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]);
             exit(EXIT_FAILURE);
         }
     } while (1);
@@ -55,20 +54,20 @@
         exit(EXIT_FAILURE);
     }
 
-    if(nosync)
-        /* also set NO_REMOUNT_RO as remount ro includes an implicit sync */
-        flags = ANDROID_RB_FLAG_NO_SYNC | ANDROID_RB_FLAG_NO_REMOUNT_RO;
+    if (argc > optind)
+        optarg = argv[optind];
 
-    if(poweroff)
-        ret = android_reboot(ANDROID_RB_POWEROFF, flags, 0);
-    else if(argc > optind)
-        ret = android_reboot(ANDROID_RB_RESTART2, flags, argv[optind]);
-    else
-        ret = android_reboot(ANDROID_RB_RESTART, flags, 0);
+    prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
+    if (prop_len >= sizeof(property_val)) {
+        fprintf(stderr, "reboot command too long: %s\n", optarg);
+        exit(EXIT_FAILURE);
+    }
+
+    ret = property_set(ANDROID_RB_PROPERTY, property_val);
     if(ret < 0) {
         perror("reboot");
         exit(EXIT_FAILURE);
     }
-    fprintf(stderr, "reboot returned\n");
+    fprintf(stderr, "Done\n");
     return 0;
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c3ef503..f783768 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -398,6 +398,9 @@
     class_reset late_start
     class_reset main
 
+on property:sys.powerctl=*
+    powerctl ${sys.powerctl}
+
 ## Daemon processes to be run by init.
 ##
 service ueventd /sbin/ueventd