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