builtins.c revision e46f9d510db9351682cf17c49115110870147335
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 <linux/loop.h>
33#include <cutils/partition_utils.h>
34#include <sys/system_properties.h>
35
36#ifdef HAVE_SELINUX
37#include <selinux/selinux.h>
38#include <selinux/label.h>
39#endif
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
51void add_environment(const char *name, const char *value);
52
53extern int init_module(void *, unsigned long, const char *);
54
55static int write_file(const char *path, const char *value)
56{
57    int fd, ret, len;
58
59    fd = open(path, O_WRONLY|O_CREAT, 0622);
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
78static int insmod(const char *filename, char *options)
79{
80    void *module;
81    unsigned size;
82    int ret;
83
84    module = read_file(filename, &size);
85    if (!module)
86        return -1;
87
88    ret = init_module(module, size, options);
89
90    free(module);
91
92    return ret;
93}
94
95static int setkey(struct kbentry *kbe)
96{
97    int fd, ret;
98
99    fd = open("/dev/tty0", O_RDWR | O_SYNC);
100    if (fd < 0)
101        return -1;
102
103    ret = ioctl(fd, KDSKBENT, kbe);
104
105    close(fd);
106    return ret;
107}
108
109static int __ifupdown(const char *interface, int up)
110{
111    struct ifreq ifr;
112    int s, ret;
113
114    strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
115
116    s = socket(AF_INET, SOCK_DGRAM, 0);
117    if (s < 0)
118        return -1;
119
120    ret = ioctl(s, SIOCGIFFLAGS, &ifr);
121    if (ret < 0) {
122        goto done;
123    }
124
125    if (up)
126        ifr.ifr_flags |= IFF_UP;
127    else
128        ifr.ifr_flags &= ~IFF_UP;
129
130    ret = ioctl(s, SIOCSIFFLAGS, &ifr);
131
132done:
133    close(s);
134    return ret;
135}
136
137static void service_start_if_not_disabled(struct service *svc)
138{
139    if (!(svc->flags & SVC_DISABLED)) {
140        service_start(svc, NULL);
141    }
142}
143
144int do_chdir(int nargs, char **args)
145{
146    chdir(args[1]);
147    return 0;
148}
149
150int do_chroot(int nargs, char **args)
151{
152    chroot(args[1]);
153    return 0;
154}
155
156int do_class_start(int nargs, char **args)
157{
158        /* Starting a class does not start services
159         * which are explicitly disabled.  They must
160         * be started individually.
161         */
162    service_for_each_class(args[1], service_start_if_not_disabled);
163    return 0;
164}
165
166int do_class_stop(int nargs, char **args)
167{
168    service_for_each_class(args[1], service_stop);
169    return 0;
170}
171
172int do_class_reset(int nargs, char **args)
173{
174    service_for_each_class(args[1], service_reset);
175    return 0;
176}
177
178int do_domainname(int nargs, char **args)
179{
180    return write_file("/proc/sys/kernel/domainname", args[1]);
181}
182
183int do_exec(int nargs, char **args)
184{
185    return -1;
186}
187
188int do_export(int nargs, char **args)
189{
190    add_environment(args[1], args[2]);
191    return 0;
192}
193
194int do_hostname(int nargs, char **args)
195{
196    return write_file("/proc/sys/kernel/hostname", args[1]);
197}
198
199int do_ifup(int nargs, char **args)
200{
201    return __ifupdown(args[1], 1);
202}
203
204
205static int do_insmod_inner(int nargs, char **args, int opt_len)
206{
207    char options[opt_len + 1];
208    int i;
209
210    options[0] = '\0';
211    if (nargs > 2) {
212        strcpy(options, args[2]);
213        for (i = 3; i < nargs; ++i) {
214            strcat(options, " ");
215            strcat(options, args[i]);
216        }
217    }
218
219    return insmod(args[1], options);
220}
221
222int do_insmod(int nargs, char **args)
223{
224    int i;
225    int size = 0;
226
227    if (nargs > 2) {
228        for (i = 2; i < nargs; ++i)
229            size += strlen(args[i]) + 1;
230    }
231
232    return do_insmod_inner(nargs, args, size);
233}
234
235int do_mkdir(int nargs, char **args)
236{
237    mode_t mode = 0755;
238    int ret;
239
240    /* mkdir <path> [mode] [owner] [group] */
241
242    if (nargs >= 3) {
243        mode = strtoul(args[2], 0, 8);
244    }
245
246    ret = mkdir(args[1], mode);
247    /* chmod in case the directory already exists */
248    if (ret == -1 && errno == EEXIST) {
249        ret = chmod(args[1], mode);
250    }
251    if (ret == -1) {
252        return -errno;
253    }
254
255    if (nargs >= 4) {
256        uid_t uid = decode_uid(args[3]);
257        gid_t gid = -1;
258
259        if (nargs == 5) {
260            gid = decode_uid(args[4]);
261        }
262
263        if (chown(args[1], uid, gid)) {
264            return -errno;
265        }
266    }
267
268    return 0;
269}
270
271static struct {
272    const char *name;
273    unsigned flag;
274} mount_flags[] = {
275    { "noatime",    MS_NOATIME },
276    { "nosuid",     MS_NOSUID },
277    { "nodev",      MS_NODEV },
278    { "nodiratime", MS_NODIRATIME },
279    { "ro",         MS_RDONLY },
280    { "rw",         0 },
281    { "remount",    MS_REMOUNT },
282    { "defaults",   0 },
283    { 0,            0 },
284};
285
286#define DATA_MNT_POINT "/data"
287
288/* mount <type> <device> <path> <flags ...> <options> */
289int do_mount(int nargs, char **args)
290{
291    char tmp[64];
292    char *source, *target, *system;
293    char *options = NULL;
294    unsigned flags = 0;
295    int n, i;
296    int wait = 0;
297
298    for (n = 4; n < nargs; n++) {
299        for (i = 0; mount_flags[i].name; i++) {
300            if (!strcmp(args[n], mount_flags[i].name)) {
301                flags |= mount_flags[i].flag;
302                break;
303            }
304        }
305
306        if (!mount_flags[i].name) {
307            if (!strcmp(args[n], "wait"))
308                wait = 1;
309            /* if our last argument isn't a flag, wolf it up as an option string */
310            else if (n + 1 == nargs)
311                options = args[n];
312        }
313    }
314
315    system = args[1];
316    source = args[2];
317    target = args[3];
318
319    if (!strncmp(source, "mtd@", 4)) {
320        n = mtd_name_to_number(source + 4);
321        if (n < 0) {
322            return -1;
323        }
324
325        sprintf(tmp, "/dev/block/mtdblock%d", n);
326
327        if (wait)
328            wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
329        if (mount(tmp, target, system, flags, options) < 0) {
330            return -1;
331        }
332
333        goto exit_success;
334    } else if (!strncmp(source, "loop@", 5)) {
335        int mode, loop, fd;
336        struct loop_info info;
337
338        mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
339        fd = open(source + 5, mode);
340        if (fd < 0) {
341            return -1;
342        }
343
344        for (n = 0; ; n++) {
345            sprintf(tmp, "/dev/block/loop%d", n);
346            loop = open(tmp, mode);
347            if (loop < 0) {
348                return -1;
349            }
350
351            /* if it is a blank loop device */
352            if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
353                /* if it becomes our loop device */
354                if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
355                    close(fd);
356
357                    if (mount(tmp, target, system, flags, options) < 0) {
358                        ioctl(loop, LOOP_CLR_FD, 0);
359                        close(loop);
360                        return -1;
361                    }
362
363                    close(loop);
364                    goto exit_success;
365                }
366            }
367
368            close(loop);
369        }
370
371        close(fd);
372        ERROR("out of loopback devices");
373        return -1;
374    } else {
375        if (wait)
376            wait_for_file(source, COMMAND_RETRY_TIMEOUT);
377        if (mount(source, target, system, flags, options) < 0) {
378            /* If this fails, it may be an encrypted filesystem
379             * or it could just be wiped.  If wiped, that will be
380             * handled later in the boot process.
381             * We only support encrypting /data.  Check
382             * if we're trying to mount it, and if so,
383             * assume it's encrypted, mount a tmpfs instead.
384             * Then save the orig mount parms in properties
385             * for vold to query when it mounts the real
386             * encrypted /data.
387             */
388            if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) {
389                const char *tmpfs_options;
390
391                tmpfs_options = property_get("ro.crypto.tmpfs_options");
392
393                if (mount("tmpfs", target, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV,
394                    tmpfs_options) < 0) {
395                    return -1;
396                }
397
398                /* Set the property that triggers the framework to do a minimal
399                 * startup and ask the user for a password
400                 */
401                property_set("ro.crypto.state", "encrypted");
402                property_set("vold.decrypt", "1");
403            } else {
404                return -1;
405            }
406        }
407
408        if (!strcmp(target, DATA_MNT_POINT)) {
409            char fs_flags[32];
410
411            /* Save the original mount options */
412            property_set("ro.crypto.fs_type", system);
413            property_set("ro.crypto.fs_real_blkdev", source);
414            property_set("ro.crypto.fs_mnt_point", target);
415            if (options) {
416                property_set("ro.crypto.fs_options", options);
417            }
418            snprintf(fs_flags, sizeof(fs_flags), "0x%8.8x", flags);
419            property_set("ro.crypto.fs_flags", fs_flags);
420        }
421    }
422
423exit_success:
424    /* If not running encrypted, then set the property saying we are
425     * unencrypted, and also trigger the action for a nonencrypted system.
426     */
427    if (!strcmp(target, DATA_MNT_POINT)) {
428        const char *prop;
429
430        prop = property_get("ro.crypto.state");
431        if (! prop) {
432            prop = "notset";
433        }
434        if (strcmp(prop, "encrypted")) {
435            property_set("ro.crypto.state", "unencrypted");
436            action_for_each_trigger("nonencrypted", action_add_queue_tail);
437        }
438    }
439
440    return 0;
441
442}
443
444int do_setcon(int nargs, char **args) {
445#ifdef HAVE_SELINUX
446    if (is_selinux_enabled() <= 0)
447        return 0;
448    if (setcon(args[1]) < 0) {
449        return -errno;
450    }
451#endif
452    return 0;
453}
454
455int do_setenforce(int nargs, char **args) {
456#ifdef HAVE_SELINUX
457    if (is_selinux_enabled() <= 0)
458        return 0;
459    if (security_setenforce(atoi(args[1])) < 0) {
460        return -errno;
461    }
462#endif
463    return 0;
464}
465
466int do_setkey(int nargs, char **args)
467{
468    struct kbentry kbe;
469    kbe.kb_table = strtoul(args[1], 0, 0);
470    kbe.kb_index = strtoul(args[2], 0, 0);
471    kbe.kb_value = strtoul(args[3], 0, 0);
472    return setkey(&kbe);
473}
474
475int do_setprop(int nargs, char **args)
476{
477    const char *name = args[1];
478    const char *value = args[2];
479    char prop_val[PROP_VALUE_MAX];
480    int ret;
481
482    ret = expand_props(prop_val, value, sizeof(prop_val));
483    if (ret) {
484        ERROR("cannot expand '%s' while assigning to '%s'\n", value, name);
485        return -EINVAL;
486    }
487    property_set(name, prop_val);
488    return 0;
489}
490
491int do_setrlimit(int nargs, char **args)
492{
493    struct rlimit limit;
494    int resource;
495    resource = atoi(args[1]);
496    limit.rlim_cur = atoi(args[2]);
497    limit.rlim_max = atoi(args[3]);
498    return setrlimit(resource, &limit);
499}
500
501int do_start(int nargs, char **args)
502{
503    struct service *svc;
504    svc = service_find_by_name(args[1]);
505    if (svc) {
506        service_start(svc, NULL);
507    }
508    return 0;
509}
510
511int do_stop(int nargs, char **args)
512{
513    struct service *svc;
514    svc = service_find_by_name(args[1]);
515    if (svc) {
516        service_stop(svc);
517    }
518    return 0;
519}
520
521int do_restart(int nargs, char **args)
522{
523    struct service *svc;
524    svc = service_find_by_name(args[1]);
525    if (svc) {
526        service_stop(svc);
527        service_start(svc, NULL);
528    }
529    return 0;
530}
531
532int do_trigger(int nargs, char **args)
533{
534    action_for_each_trigger(args[1], action_add_queue_tail);
535    return 0;
536}
537
538int do_symlink(int nargs, char **args)
539{
540    return symlink(args[1], args[2]);
541}
542
543int do_rm(int nargs, char **args)
544{
545    return unlink(args[1]);
546}
547
548int do_rmdir(int nargs, char **args)
549{
550    return rmdir(args[1]);
551}
552
553int do_sysclktz(int nargs, char **args)
554{
555    struct timezone tz;
556
557    if (nargs != 2)
558        return -1;
559
560    memset(&tz, 0, sizeof(tz));
561    tz.tz_minuteswest = atoi(args[1]);
562    if (settimeofday(NULL, &tz))
563        return -1;
564    return 0;
565}
566
567int do_write(int nargs, char **args)
568{
569    const char *path = args[1];
570    const char *value = args[2];
571    char prop_val[PROP_VALUE_MAX];
572    int ret;
573
574    ret = expand_props(prop_val, value, sizeof(prop_val));
575    if (ret) {
576        ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
577        return -EINVAL;
578    }
579    return write_file(path, prop_val);
580}
581
582int do_copy(int nargs, char **args)
583{
584    char *buffer = NULL;
585    int rc = 0;
586    int fd1 = -1, fd2 = -1;
587    struct stat info;
588    int brtw, brtr;
589    char *p;
590
591    if (nargs != 3)
592        return -1;
593
594    if (stat(args[1], &info) < 0)
595        return -1;
596
597    if ((fd1 = open(args[1], O_RDONLY)) < 0)
598        goto out_err;
599
600    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
601        goto out_err;
602
603    if (!(buffer = malloc(info.st_size)))
604        goto out_err;
605
606    p = buffer;
607    brtr = info.st_size;
608    while(brtr) {
609        rc = read(fd1, p, brtr);
610        if (rc < 0)
611            goto out_err;
612        if (rc == 0)
613            break;
614        p += rc;
615        brtr -= rc;
616    }
617
618    p = buffer;
619    brtw = info.st_size;
620    while(brtw) {
621        rc = write(fd2, p, brtw);
622        if (rc < 0)
623            goto out_err;
624        if (rc == 0)
625            break;
626        p += rc;
627        brtw -= rc;
628    }
629
630    rc = 0;
631    goto out;
632out_err:
633    rc = -1;
634out:
635    if (buffer)
636        free(buffer);
637    if (fd1 >= 0)
638        close(fd1);
639    if (fd2 >= 0)
640        close(fd2);
641    return rc;
642}
643
644int do_chown(int nargs, char **args) {
645    /* GID is optional. */
646    if (nargs == 3) {
647        if (chown(args[2], decode_uid(args[1]), -1) < 0)
648            return -errno;
649    } else if (nargs == 4) {
650        if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
651            return -errno;
652    } else {
653        return -1;
654    }
655    return 0;
656}
657
658static mode_t get_mode(const char *s) {
659    mode_t mode = 0;
660    while (*s) {
661        if (*s >= '0' && *s <= '7') {
662            mode = (mode<<3) | (*s-'0');
663        } else {
664            return -1;
665        }
666        s++;
667    }
668    return mode;
669}
670
671int do_chmod(int nargs, char **args) {
672    mode_t mode = get_mode(args[1]);
673    if (chmod(args[2], mode) < 0) {
674        return -errno;
675    }
676    return 0;
677}
678
679int do_restorecon(int nargs, char **args) {
680#ifdef HAVE_SELINUX
681    char *secontext = NULL;
682    struct stat sb;
683    int i;
684
685    if (is_selinux_enabled() <= 0 || !sehandle)
686        return 0;
687
688    for (i = 1; i < nargs; i++) {
689        if (lstat(args[i], &sb) < 0)
690            return -errno;
691        if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0)
692            return -errno;
693        if (lsetfilecon(args[i], secontext) < 0) {
694            freecon(secontext);
695            return -errno;
696        }
697        freecon(secontext);
698    }
699#endif
700    return 0;
701}
702
703int do_setsebool(int nargs, char **args) {
704#ifdef HAVE_SELINUX
705    SELboolean *b = alloca(nargs * sizeof(SELboolean));
706    char *v;
707    int i;
708
709    if (is_selinux_enabled() <= 0)
710        return 0;
711
712    for (i = 1; i < nargs; i++) {
713        char *name = args[i];
714        v = strchr(name, '=');
715        if (!v) {
716            ERROR("setsebool: argument %s had no =\n", name);
717            return -EINVAL;
718        }
719        *v++ = 0;
720        b[i-1].name = name;
721        if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
722            b[i-1].value = 1;
723        else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
724            b[i-1].value = 0;
725        else {
726            ERROR("setsebool: invalid value %s\n", v);
727            return -EINVAL;
728        }
729    }
730
731    if (security_set_boolean_list(nargs - 1, b, 0) < 0)
732        return -errno;
733#endif
734    return 0;
735}
736
737int do_loglevel(int nargs, char **args) {
738    if (nargs == 2) {
739        klog_set_level(atoi(args[1]));
740        return 0;
741    }
742    return -1;
743}
744
745int do_load_persist_props(int nargs, char **args) {
746    if (nargs == 1) {
747        load_persist_props();
748        return 0;
749    }
750    return -1;
751}
752
753int do_wait(int nargs, char **args)
754{
755    if (nargs == 2) {
756        return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
757    }
758    return -1;
759}
760