Home | History | Annotate | Download | only in init
      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 <sys/types.h>
     18 #include <sys/stat.h>
     19 #include <fcntl.h>
     20 #include <unistd.h>
     21 #include <string.h>
     22 #include <stdio.h>
     23 #include <linux/kd.h>
     24 #include <errno.h>
     25 #include <sys/socket.h>
     26 #include <netinet/in.h>
     27 #include <linux/if.h>
     28 #include <arpa/inet.h>
     29 #include <stdlib.h>
     30 #include <sys/mount.h>
     31 #include <sys/resource.h>
     32 #include <sys/wait.h>
     33 #include <linux/loop.h>
     34 #include <cutils/partition_utils.h>
     35 #include <cutils/android_reboot.h>
     36 #include <fs_mgr.h>
     37 
     38 #include <selinux/selinux.h>
     39 #include <selinux/label.h>
     40 
     41 #include "init.h"
     42 #include "keywords.h"
     43 #include "property_service.h"
     44 #include "devices.h"
     45 #include "init_parser.h"
     46 #include "util.h"
     47 #include "log.h"
     48 
     49 #include <private/android_filesystem_config.h>
     50 
     51 int add_environment(const char *name, const char *value);
     52 
     53 extern int init_module(void *, unsigned long, const char *);
     54 
     55 static int write_file(const char *path, const char *value)
     56 {
     57     int fd, ret, len;
     58 
     59     fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW, 0600);
     60 
     61     if (fd < 0)
     62         return -errno;
     63 
     64     len = strlen(value);
     65 
     66     do {
     67         ret = write(fd, value, len);
     68     } while (ret < 0 && errno == EINTR);
     69 
     70     close(fd);
     71     if (ret < 0) {
     72         return -errno;
     73     } else {
     74         return 0;
     75     }
     76 }
     77 
     78 static int _open(const char *path)
     79 {
     80     int fd;
     81 
     82     fd = open(path, O_RDONLY | O_NOFOLLOW);
     83     if (fd < 0)
     84         fd = open(path, O_WRONLY | O_NOFOLLOW);
     85 
     86     return fd;
     87 }
     88 
     89 static int _chown(const char *path, unsigned int uid, unsigned int gid)
     90 {
     91     int fd;
     92     int ret;
     93 
     94     fd = _open(path);
     95     if (fd < 0) {
     96         return -1;
     97     }
     98 
     99     ret = fchown(fd, uid, gid);
    100     if (ret < 0) {
    101         int errno_copy = errno;
    102         close(fd);
    103         errno = errno_copy;
    104         return -1;
    105     }
    106 
    107     close(fd);
    108 
    109     return 0;
    110 }
    111 
    112 static int _chmod(const char *path, mode_t mode)
    113 {
    114     int fd;
    115     int ret;
    116 
    117     fd = _open(path);
    118     if (fd < 0) {
    119         return -1;
    120     }
    121 
    122     ret = fchmod(fd, mode);
    123     if (ret < 0) {
    124         int errno_copy = errno;
    125         close(fd);
    126         errno = errno_copy;
    127         return -1;
    128     }
    129 
    130     close(fd);
    131 
    132     return 0;
    133 }
    134 
    135 static int insmod(const char *filename, char *options)
    136 {
    137     void *module;
    138     unsigned size;
    139     int ret;
    140 
    141     module = read_file(filename, &size);
    142     if (!module)
    143         return -1;
    144 
    145     ret = init_module(module, size, options);
    146 
    147     free(module);
    148 
    149     return ret;
    150 }
    151 
    152 static int setkey(struct kbentry *kbe)
    153 {
    154     int fd, ret;
    155 
    156     fd = open("/dev/tty0", O_RDWR | O_SYNC);
    157     if (fd < 0)
    158         return -1;
    159 
    160     ret = ioctl(fd, KDSKBENT, kbe);
    161 
    162     close(fd);
    163     return ret;
    164 }
    165 
    166 static int __ifupdown(const char *interface, int up)
    167 {
    168     struct ifreq ifr;
    169     int s, ret;
    170 
    171     strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
    172 
    173     s = socket(AF_INET, SOCK_DGRAM, 0);
    174     if (s < 0)
    175         return -1;
    176 
    177     ret = ioctl(s, SIOCGIFFLAGS, &ifr);
    178     if (ret < 0) {
    179         goto done;
    180     }
    181 
    182     if (up)
    183         ifr.ifr_flags |= IFF_UP;
    184     else
    185         ifr.ifr_flags &= ~IFF_UP;
    186 
    187     ret = ioctl(s, SIOCSIFFLAGS, &ifr);
    188 
    189 done:
    190     close(s);
    191     return ret;
    192 }
    193 
    194 static void service_start_if_not_disabled(struct service *svc)
    195 {
    196     if (!(svc->flags & SVC_DISABLED)) {
    197         service_start(svc, NULL);
    198     } else {
    199         svc->flags |= SVC_DISABLED_START;
    200     }
    201 }
    202 
    203 int do_chdir(int nargs, char **args)
    204 {
    205     chdir(args[1]);
    206     return 0;
    207 }
    208 
    209 int do_chroot(int nargs, char **args)
    210 {
    211     chroot(args[1]);
    212     return 0;
    213 }
    214 
    215 int do_class_start(int nargs, char **args)
    216 {
    217         /* Starting a class does not start services
    218          * which are explicitly disabled.  They must
    219          * be started individually.
    220          */
    221     service_for_each_class(args[1], service_start_if_not_disabled);
    222     return 0;
    223 }
    224 
    225 int do_class_stop(int nargs, char **args)
    226 {
    227     service_for_each_class(args[1], service_stop);
    228     return 0;
    229 }
    230 
    231 int do_class_reset(int nargs, char **args)
    232 {
    233     service_for_each_class(args[1], service_reset);
    234     return 0;
    235 }
    236 
    237 int do_domainname(int nargs, char **args)
    238 {
    239     return write_file("/proc/sys/kernel/domainname", args[1]);
    240 }
    241 
    242 int do_enable(int nargs, char **args)
    243 {
    244     struct service *svc;
    245     svc = service_find_by_name(args[1]);
    246     if (svc) {
    247         svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED);
    248         if (svc->flags & SVC_DISABLED_START) {
    249             service_start(svc, NULL);
    250         }
    251     } else {
    252         return -1;
    253     }
    254     return 0;
    255 }
    256 
    257 int do_exec(int nargs, char **args)
    258 {
    259     return -1;
    260 }
    261 
    262 int do_export(int nargs, char **args)
    263 {
    264     return add_environment(args[1], args[2]);
    265 }
    266 
    267 int do_hostname(int nargs, char **args)
    268 {
    269     return write_file("/proc/sys/kernel/hostname", args[1]);
    270 }
    271 
    272 int do_ifup(int nargs, char **args)
    273 {
    274     return __ifupdown(args[1], 1);
    275 }
    276 
    277 
    278 static int do_insmod_inner(int nargs, char **args, int opt_len)
    279 {
    280     char options[opt_len + 1];
    281     int i;
    282 
    283     options[0] = '\0';
    284     if (nargs > 2) {
    285         strcpy(options, args[2]);
    286         for (i = 3; i < nargs; ++i) {
    287             strcat(options, " ");
    288             strcat(options, args[i]);
    289         }
    290     }
    291 
    292     return insmod(args[1], options);
    293 }
    294 
    295 int do_insmod(int nargs, char **args)
    296 {
    297     int i;
    298     int size = 0;
    299 
    300     if (nargs > 2) {
    301         for (i = 2; i < nargs; ++i)
    302             size += strlen(args[i]) + 1;
    303     }
    304 
    305     return do_insmod_inner(nargs, args, size);
    306 }
    307 
    308 int do_mkdir(int nargs, char **args)
    309 {
    310     mode_t mode = 0755;
    311     int ret;
    312 
    313     /* mkdir <path> [mode] [owner] [group] */
    314 
    315     if (nargs >= 3) {
    316         mode = strtoul(args[2], 0, 8);
    317     }
    318 
    319     ret = make_dir(args[1], mode);
    320     /* chmod in case the directory already exists */
    321     if (ret == -1 && errno == EEXIST) {
    322         ret = _chmod(args[1], mode);
    323     }
    324     if (ret == -1) {
    325         return -errno;
    326     }
    327 
    328     if (nargs >= 4) {
    329         uid_t uid = decode_uid(args[3]);
    330         gid_t gid = -1;
    331 
    332         if (nargs == 5) {
    333             gid = decode_uid(args[4]);
    334         }
    335 
    336         if (_chown(args[1], uid, gid) < 0) {
    337             return -errno;
    338         }
    339 
    340         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
    341         if (mode & (S_ISUID | S_ISGID)) {
    342             ret = _chmod(args[1], mode);
    343             if (ret == -1) {
    344                 return -errno;
    345             }
    346         }
    347     }
    348 
    349     return 0;
    350 }
    351 
    352 static struct {
    353     const char *name;
    354     unsigned flag;
    355 } mount_flags[] = {
    356     { "noatime",    MS_NOATIME },
    357     { "noexec",     MS_NOEXEC },
    358     { "nosuid",     MS_NOSUID },
    359     { "nodev",      MS_NODEV },
    360     { "nodiratime", MS_NODIRATIME },
    361     { "ro",         MS_RDONLY },
    362     { "rw",         0 },
    363     { "remount",    MS_REMOUNT },
    364     { "bind",       MS_BIND },
    365     { "rec",        MS_REC },
    366     { "unbindable", MS_UNBINDABLE },
    367     { "private",    MS_PRIVATE },
    368     { "slave",      MS_SLAVE },
    369     { "shared",     MS_SHARED },
    370     { "defaults",   0 },
    371     { 0,            0 },
    372 };
    373 
    374 #define DATA_MNT_POINT "/data"
    375 
    376 /* mount <type> <device> <path> <flags ...> <options> */
    377 int do_mount(int nargs, char **args)
    378 {
    379     char tmp[64];
    380     char *source, *target, *system;
    381     char *options = NULL;
    382     unsigned flags = 0;
    383     int n, i;
    384     int wait = 0;
    385 
    386     for (n = 4; n < nargs; n++) {
    387         for (i = 0; mount_flags[i].name; i++) {
    388             if (!strcmp(args[n], mount_flags[i].name)) {
    389                 flags |= mount_flags[i].flag;
    390                 break;
    391             }
    392         }
    393 
    394         if (!mount_flags[i].name) {
    395             if (!strcmp(args[n], "wait"))
    396                 wait = 1;
    397             /* if our last argument isn't a flag, wolf it up as an option string */
    398             else if (n + 1 == nargs)
    399                 options = args[n];
    400         }
    401     }
    402 
    403     system = args[1];
    404     source = args[2];
    405     target = args[3];
    406 
    407     if (!strncmp(source, "mtd@", 4)) {
    408         n = mtd_name_to_number(source + 4);
    409         if (n < 0) {
    410             return -1;
    411         }
    412 
    413         sprintf(tmp, "/dev/block/mtdblock%d", n);
    414 
    415         if (wait)
    416             wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
    417         if (mount(tmp, target, system, flags, options) < 0) {
    418             return -1;
    419         }
    420 
    421         goto exit_success;
    422     } else if (!strncmp(source, "loop@", 5)) {
    423         int mode, loop, fd;
    424         struct loop_info info;
    425 
    426         mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
    427         fd = open(source + 5, mode);
    428         if (fd < 0) {
    429             return -1;
    430         }
    431 
    432         for (n = 0; ; n++) {
    433             sprintf(tmp, "/dev/block/loop%d", n);
    434             loop = open(tmp, mode);
    435             if (loop < 0) {
    436                 return -1;
    437             }
    438 
    439             /* if it is a blank loop device */
    440             if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
    441                 /* if it becomes our loop device */
    442                 if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
    443                     close(fd);
    444 
    445                     if (mount(tmp, target, system, flags, options) < 0) {
    446                         ioctl(loop, LOOP_CLR_FD, 0);
    447                         close(loop);
    448                         return -1;
    449                     }
    450 
    451                     close(loop);
    452                     goto exit_success;
    453                 }
    454             }
    455 
    456             close(loop);
    457         }
    458 
    459         close(fd);
    460         ERROR("out of loopback devices");
    461         return -1;
    462     } else {
    463         if (wait)
    464             wait_for_file(source, COMMAND_RETRY_TIMEOUT);
    465         if (mount(source, target, system, flags, options) < 0) {
    466             return -1;
    467         }
    468 
    469     }
    470 
    471 exit_success:
    472     return 0;
    473 
    474 }
    475 
    476 static int wipe_data_via_recovery()
    477 {
    478     mkdir("/cache/recovery", 0700);
    479     int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
    480     if (fd >= 0) {
    481         write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
    482         write(fd, "--reason=wipe_data_via_recovery\n", strlen("--reason=wipe_data_via_recovery\n") + 1);
    483         close(fd);
    484     } else {
    485         ERROR("could not open /cache/recovery/command\n");
    486         return -1;
    487     }
    488     android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
    489     while (1) { pause(); }  // never reached
    490 }
    491 
    492 
    493 /*
    494  * This function might request a reboot, in which case it will
    495  * not return.
    496  */
    497 int do_mount_all(int nargs, char **args)
    498 {
    499     pid_t pid;
    500     int ret = -1;
    501     int child_ret = -1;
    502     int status;
    503     const char *prop;
    504     struct fstab *fstab;
    505 
    506     if (nargs != 2) {
    507         return -1;
    508     }
    509 
    510     /*
    511      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
    512      * do the call in the child to provide protection to the main init
    513      * process if anything goes wrong (crash or memory leak), and wait for
    514      * the child to finish in the parent.
    515      */
    516     pid = fork();
    517     if (pid > 0) {
    518         /* Parent.  Wait for the child to return */
    519         int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
    520         if (wp_ret < 0) {
    521             /* Unexpected error code. We will continue anyway. */
    522             NOTICE("waitpid failed rc=%d, errno=%d\n", wp_ret, errno);
    523         }
    524 
    525         if (WIFEXITED(status)) {
    526             ret = WEXITSTATUS(status);
    527         } else {
    528             ret = -1;
    529         }
    530     } else if (pid == 0) {
    531         /* child, call fs_mgr_mount_all() */
    532         klog_set_level(6);  /* So we can see what fs_mgr_mount_all() does */
    533         fstab = fs_mgr_read_fstab(args[1]);
    534         child_ret = fs_mgr_mount_all(fstab);
    535         fs_mgr_free_fstab(fstab);
    536         if (child_ret == -1) {
    537             ERROR("fs_mgr_mount_all returned an error\n");
    538         }
    539         _exit(child_ret);
    540     } else {
    541         /* fork failed, return an error */
    542         return -1;
    543     }
    544 
    545     if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
    546         property_set("vold.decrypt", "trigger_encryption");
    547     } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
    548         property_set("ro.crypto.state", "encrypted");
    549         property_set("vold.decrypt", "trigger_default_encryption");
    550     } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
    551         property_set("ro.crypto.state", "unencrypted");
    552         /* If fs_mgr determined this is an unencrypted device, then trigger
    553          * that action.
    554          */
    555         action_for_each_trigger("nonencrypted", action_add_queue_tail);
    556     } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
    557         /* Setup a wipe via recovery, and reboot into recovery */
    558         ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
    559         ret = wipe_data_via_recovery();
    560         /* If reboot worked, there is no return. */
    561     } else if (ret > 0) {
    562         ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
    563     }
    564     /* else ... < 0: error */
    565 
    566     return ret;
    567 }
    568 
    569 int do_swapon_all(int nargs, char **args)
    570 {
    571     struct fstab *fstab;
    572     int ret;
    573 
    574     fstab = fs_mgr_read_fstab(args[1]);
    575     ret = fs_mgr_swapon_all(fstab);
    576     fs_mgr_free_fstab(fstab);
    577 
    578     return ret;
    579 }
    580 
    581 int do_setcon(int nargs, char **args) {
    582     if (is_selinux_enabled() <= 0)
    583         return 0;
    584     if (setcon(args[1]) < 0) {
    585         return -errno;
    586     }
    587     return 0;
    588 }
    589 
    590 int do_setenforce(int nargs, char **args) {
    591     if (is_selinux_enabled() <= 0)
    592         return 0;
    593     if (security_setenforce(atoi(args[1])) < 0) {
    594         return -errno;
    595     }
    596     return 0;
    597 }
    598 
    599 int do_setkey(int nargs, char **args)
    600 {
    601     struct kbentry kbe;
    602     kbe.kb_table = strtoul(args[1], 0, 0);
    603     kbe.kb_index = strtoul(args[2], 0, 0);
    604     kbe.kb_value = strtoul(args[3], 0, 0);
    605     return setkey(&kbe);
    606 }
    607 
    608 int do_setprop(int nargs, char **args)
    609 {
    610     const char *name = args[1];
    611     const char *value = args[2];
    612     char prop_val[PROP_VALUE_MAX];
    613     int ret;
    614 
    615     ret = expand_props(prop_val, value, sizeof(prop_val));
    616     if (ret) {
    617         ERROR("cannot expand '%s' while assigning to '%s'\n", value, name);
    618         return -EINVAL;
    619     }
    620     property_set(name, prop_val);
    621     return 0;
    622 }
    623 
    624 int do_setrlimit(int nargs, char **args)
    625 {
    626     struct rlimit limit;
    627     int resource;
    628     resource = atoi(args[1]);
    629     limit.rlim_cur = atoi(args[2]);
    630     limit.rlim_max = atoi(args[3]);
    631     return setrlimit(resource, &limit);
    632 }
    633 
    634 int do_start(int nargs, char **args)
    635 {
    636     struct service *svc;
    637     svc = service_find_by_name(args[1]);
    638     if (svc) {
    639         service_start(svc, NULL);
    640     }
    641     return 0;
    642 }
    643 
    644 int do_stop(int nargs, char **args)
    645 {
    646     struct service *svc;
    647     svc = service_find_by_name(args[1]);
    648     if (svc) {
    649         service_stop(svc);
    650     }
    651     return 0;
    652 }
    653 
    654 int do_restart(int nargs, char **args)
    655 {
    656     struct service *svc;
    657     svc = service_find_by_name(args[1]);
    658     if (svc) {
    659         service_restart(svc);
    660     }
    661     return 0;
    662 }
    663 
    664 int do_powerctl(int nargs, char **args)
    665 {
    666     char command[PROP_VALUE_MAX];
    667     int res;
    668     int len = 0;
    669     int cmd = 0;
    670     char *reboot_target;
    671 
    672     res = expand_props(command, args[1], sizeof(command));
    673     if (res) {
    674         ERROR("powerctl: cannot expand '%s'\n", args[1]);
    675         return -EINVAL;
    676     }
    677 
    678     if (strncmp(command, "shutdown", 8) == 0) {
    679         cmd = ANDROID_RB_POWEROFF;
    680         len = 8;
    681     } else if (strncmp(command, "reboot", 6) == 0) {
    682         cmd = ANDROID_RB_RESTART2;
    683         len = 6;
    684     } else {
    685         ERROR("powerctl: unrecognized command '%s'\n", command);
    686         return -EINVAL;
    687     }
    688 
    689     if (command[len] == ',') {
    690         reboot_target = &command[len + 1];
    691     } else if (command[len] == '\0') {
    692         reboot_target = "";
    693     } else {
    694         ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
    695         return -EINVAL;
    696     }
    697 
    698     return android_reboot(cmd, 0, reboot_target);
    699 }
    700 
    701 int do_trigger(int nargs, char **args)
    702 {
    703     action_for_each_trigger(args[1], action_add_queue_tail);
    704     return 0;
    705 }
    706 
    707 int do_symlink(int nargs, char **args)
    708 {
    709     return symlink(args[1], args[2]);
    710 }
    711 
    712 int do_rm(int nargs, char **args)
    713 {
    714     return unlink(args[1]);
    715 }
    716 
    717 int do_rmdir(int nargs, char **args)
    718 {
    719     return rmdir(args[1]);
    720 }
    721 
    722 int do_sysclktz(int nargs, char **args)
    723 {
    724     struct timezone tz;
    725 
    726     if (nargs != 2)
    727         return -1;
    728 
    729     memset(&tz, 0, sizeof(tz));
    730     tz.tz_minuteswest = atoi(args[1]);
    731     if (settimeofday(NULL, &tz))
    732         return -1;
    733     return 0;
    734 }
    735 
    736 int do_write(int nargs, char **args)
    737 {
    738     const char *path = args[1];
    739     const char *value = args[2];
    740     char prop_val[PROP_VALUE_MAX];
    741     int ret;
    742 
    743     ret = expand_props(prop_val, value, sizeof(prop_val));
    744     if (ret) {
    745         ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
    746         return -EINVAL;
    747     }
    748     return write_file(path, prop_val);
    749 }
    750 
    751 int do_copy(int nargs, char **args)
    752 {
    753     char *buffer = NULL;
    754     int rc = 0;
    755     int fd1 = -1, fd2 = -1;
    756     struct stat info;
    757     int brtw, brtr;
    758     char *p;
    759 
    760     if (nargs != 3)
    761         return -1;
    762 
    763     if (stat(args[1], &info) < 0)
    764         return -1;
    765 
    766     if ((fd1 = open(args[1], O_RDONLY)) < 0)
    767         goto out_err;
    768 
    769     if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
    770         goto out_err;
    771 
    772     if (!(buffer = malloc(info.st_size)))
    773         goto out_err;
    774 
    775     p = buffer;
    776     brtr = info.st_size;
    777     while(brtr) {
    778         rc = read(fd1, p, brtr);
    779         if (rc < 0)
    780             goto out_err;
    781         if (rc == 0)
    782             break;
    783         p += rc;
    784         brtr -= rc;
    785     }
    786 
    787     p = buffer;
    788     brtw = info.st_size;
    789     while(brtw) {
    790         rc = write(fd2, p, brtw);
    791         if (rc < 0)
    792             goto out_err;
    793         if (rc == 0)
    794             break;
    795         p += rc;
    796         brtw -= rc;
    797     }
    798 
    799     rc = 0;
    800     goto out;
    801 out_err:
    802     rc = -1;
    803 out:
    804     if (buffer)
    805         free(buffer);
    806     if (fd1 >= 0)
    807         close(fd1);
    808     if (fd2 >= 0)
    809         close(fd2);
    810     return rc;
    811 }
    812 
    813 int do_chown(int nargs, char **args) {
    814     /* GID is optional. */
    815     if (nargs == 3) {
    816         if (_chown(args[2], decode_uid(args[1]), -1) < 0)
    817             return -errno;
    818     } else if (nargs == 4) {
    819         if (_chown(args[3], decode_uid(args[1]), decode_uid(args[2])) < 0)
    820             return -errno;
    821     } else {
    822         return -1;
    823     }
    824     return 0;
    825 }
    826 
    827 static mode_t get_mode(const char *s) {
    828     mode_t mode = 0;
    829     while (*s) {
    830         if (*s >= '0' && *s <= '7') {
    831             mode = (mode<<3) | (*s-'0');
    832         } else {
    833             return -1;
    834         }
    835         s++;
    836     }
    837     return mode;
    838 }
    839 
    840 int do_chmod(int nargs, char **args) {
    841     mode_t mode = get_mode(args[1]);
    842     if (_chmod(args[2], mode) < 0) {
    843         return -errno;
    844     }
    845     return 0;
    846 }
    847 
    848 int do_restorecon(int nargs, char **args) {
    849     int i;
    850     int ret = 0;
    851 
    852     for (i = 1; i < nargs; i++) {
    853         if (restorecon(args[i]) < 0)
    854             ret = -errno;
    855     }
    856     return ret;
    857 }
    858 
    859 int do_restorecon_recursive(int nargs, char **args) {
    860     int i;
    861     int ret = 0;
    862 
    863     for (i = 1; i < nargs; i++) {
    864         if (restorecon_recursive(args[i]) < 0)
    865             ret = -errno;
    866     }
    867     return ret;
    868 }
    869 
    870 int do_setsebool(int nargs, char **args) {
    871     const char *name = args[1];
    872     const char *value = args[2];
    873     SELboolean b;
    874     int ret;
    875 
    876     if (is_selinux_enabled() <= 0)
    877         return 0;
    878 
    879     b.name = name;
    880     if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on"))
    881         b.value = 1;
    882     else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off"))
    883         b.value = 0;
    884     else {
    885         ERROR("setsebool: invalid value %s\n", value);
    886         return -EINVAL;
    887     }
    888 
    889     if (security_set_boolean_list(1, &b, 0) < 0) {
    890         ret = -errno;
    891         ERROR("setsebool: could not set %s to %s\n", name, value);
    892         return ret;
    893     }
    894 
    895     return 0;
    896 }
    897 
    898 int do_loglevel(int nargs, char **args) {
    899     int log_level;
    900     char log_level_str[PROP_VALUE_MAX] = "";
    901     if (nargs != 2) {
    902         ERROR("loglevel: missing argument\n");
    903         return -EINVAL;
    904     }
    905 
    906     if (expand_props(log_level_str, args[1], sizeof(log_level_str))) {
    907         ERROR("loglevel: cannot expand '%s'\n", args[1]);
    908         return -EINVAL;
    909     }
    910     log_level = atoi(log_level_str);
    911     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
    912         ERROR("loglevel: invalid log level'%d'\n", log_level);
    913         return -EINVAL;
    914     }
    915     klog_set_level(log_level);
    916     return 0;
    917 }
    918 
    919 int do_load_persist_props(int nargs, char **args) {
    920     if (nargs == 1) {
    921         load_persist_props();
    922         return 0;
    923     }
    924     return -1;
    925 }
    926 
    927 int do_load_all_props(int nargs, char **args) {
    928     if (nargs == 1) {
    929         load_all_props();
    930         return 0;
    931     }
    932     return -1;
    933 }
    934 
    935 int do_wait(int nargs, char **args)
    936 {
    937     if (nargs == 2) {
    938         return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
    939     } else if (nargs == 3) {
    940         return wait_for_file(args[1], atoi(args[2]));
    941     } else
    942         return -1;
    943 }
    944