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