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