builtins.c revision b6ee25e3ad4cffa2b0f5bb734df4b503e1e367ba
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    { "noexec",     MS_NOEXEC },
277    { "nosuid",     MS_NOSUID },
278    { "nodev",      MS_NODEV },
279    { "nodiratime", MS_NODIRATIME },
280    { "ro",         MS_RDONLY },
281    { "rw",         0 },
282    { "remount",    MS_REMOUNT },
283    { "defaults",   0 },
284    { 0,            0 },
285};
286
287#define DATA_MNT_POINT "/data"
288
289/* mount <type> <device> <path> <flags ...> <options> */
290int do_mount(int nargs, char **args)
291{
292    char tmp[64];
293    char *source, *target, *system;
294    char *options = NULL;
295    unsigned flags = 0;
296    int n, i;
297    int wait = 0;
298
299    for (n = 4; n < nargs; n++) {
300        for (i = 0; mount_flags[i].name; i++) {
301            if (!strcmp(args[n], mount_flags[i].name)) {
302                flags |= mount_flags[i].flag;
303                break;
304            }
305        }
306
307        if (!mount_flags[i].name) {
308            if (!strcmp(args[n], "wait"))
309                wait = 1;
310            /* if our last argument isn't a flag, wolf it up as an option string */
311            else if (n + 1 == nargs)
312                options = args[n];
313        }
314    }
315
316    system = args[1];
317    source = args[2];
318    target = args[3];
319
320    if (!strncmp(source, "mtd@", 4)) {
321        n = mtd_name_to_number(source + 4);
322        if (n < 0) {
323            return -1;
324        }
325
326        sprintf(tmp, "/dev/block/mtdblock%d", n);
327
328        if (wait)
329            wait_for_file(tmp, COMMAND_RETRY_TIMEOUT);
330        if (mount(tmp, target, system, flags, options) < 0) {
331            return -1;
332        }
333
334        goto exit_success;
335    } else if (!strncmp(source, "loop@", 5)) {
336        int mode, loop, fd;
337        struct loop_info info;
338
339        mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
340        fd = open(source + 5, mode);
341        if (fd < 0) {
342            return -1;
343        }
344
345        for (n = 0; ; n++) {
346            sprintf(tmp, "/dev/block/loop%d", n);
347            loop = open(tmp, mode);
348            if (loop < 0) {
349                return -1;
350            }
351
352            /* if it is a blank loop device */
353            if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
354                /* if it becomes our loop device */
355                if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
356                    close(fd);
357
358                    if (mount(tmp, target, system, flags, options) < 0) {
359                        ioctl(loop, LOOP_CLR_FD, 0);
360                        close(loop);
361                        return -1;
362                    }
363
364                    close(loop);
365                    goto exit_success;
366                }
367            }
368
369            close(loop);
370        }
371
372        close(fd);
373        ERROR("out of loopback devices");
374        return -1;
375    } else {
376        if (wait)
377            wait_for_file(source, COMMAND_RETRY_TIMEOUT);
378        if (mount(source, target, system, flags, options) < 0) {
379            /* If this fails, it may be an encrypted filesystem
380             * or it could just be wiped.  If wiped, that will be
381             * handled later in the boot process.
382             * We only support encrypting /data.  Check
383             * if we're trying to mount it, and if so,
384             * assume it's encrypted, mount a tmpfs instead.
385             * Then save the orig mount parms in properties
386             * for vold to query when it mounts the real
387             * encrypted /data.
388             */
389            if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) {
390                const char *tmpfs_options;
391
392                tmpfs_options = property_get("ro.crypto.tmpfs_options");
393
394                if (mount("tmpfs", target, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV,
395                    tmpfs_options) < 0) {
396                    return -1;
397                }
398
399                /* Set the property that triggers the framework to do a minimal
400                 * startup and ask the user for a password
401                 */
402                property_set("ro.crypto.state", "encrypted");
403                property_set("vold.decrypt", "1");
404            } else {
405                return -1;
406            }
407        }
408
409        if (!strcmp(target, DATA_MNT_POINT)) {
410            char fs_flags[32];
411
412            /* Save the original mount options */
413            property_set("ro.crypto.fs_type", system);
414            property_set("ro.crypto.fs_real_blkdev", source);
415            property_set("ro.crypto.fs_mnt_point", target);
416            if (options) {
417                property_set("ro.crypto.fs_options", options);
418            }
419            snprintf(fs_flags, sizeof(fs_flags), "0x%8.8x", flags);
420            property_set("ro.crypto.fs_flags", fs_flags);
421        }
422    }
423
424exit_success:
425    /* If not running encrypted, then set the property saying we are
426     * unencrypted, and also trigger the action for a nonencrypted system.
427     */
428    if (!strcmp(target, DATA_MNT_POINT)) {
429        const char *prop;
430
431        prop = property_get("ro.crypto.state");
432        if (! prop) {
433            prop = "notset";
434        }
435        if (strcmp(prop, "encrypted")) {
436            property_set("ro.crypto.state", "unencrypted");
437            action_for_each_trigger("nonencrypted", action_add_queue_tail);
438        }
439    }
440
441    return 0;
442
443}
444
445int do_setcon(int nargs, char **args) {
446#ifdef HAVE_SELINUX
447    if (is_selinux_enabled() <= 0)
448        return 0;
449    if (setcon(args[1]) < 0) {
450        return -errno;
451    }
452#endif
453    return 0;
454}
455
456int do_setenforce(int nargs, char **args) {
457#ifdef HAVE_SELINUX
458    if (is_selinux_enabled() <= 0)
459        return 0;
460    if (security_setenforce(atoi(args[1])) < 0) {
461        return -errno;
462    }
463#endif
464    return 0;
465}
466
467int do_setkey(int nargs, char **args)
468{
469    struct kbentry kbe;
470    kbe.kb_table = strtoul(args[1], 0, 0);
471    kbe.kb_index = strtoul(args[2], 0, 0);
472    kbe.kb_value = strtoul(args[3], 0, 0);
473    return setkey(&kbe);
474}
475
476int do_setprop(int nargs, char **args)
477{
478    const char *name = args[1];
479    const char *value = args[2];
480    char prop_val[PROP_VALUE_MAX];
481    int ret;
482
483    ret = expand_props(prop_val, value, sizeof(prop_val));
484    if (ret) {
485        ERROR("cannot expand '%s' while assigning to '%s'\n", value, name);
486        return -EINVAL;
487    }
488    property_set(name, prop_val);
489    return 0;
490}
491
492int do_setrlimit(int nargs, char **args)
493{
494    struct rlimit limit;
495    int resource;
496    resource = atoi(args[1]);
497    limit.rlim_cur = atoi(args[2]);
498    limit.rlim_max = atoi(args[3]);
499    return setrlimit(resource, &limit);
500}
501
502int do_start(int nargs, char **args)
503{
504    struct service *svc;
505    svc = service_find_by_name(args[1]);
506    if (svc) {
507        service_start(svc, NULL);
508    }
509    return 0;
510}
511
512int do_stop(int nargs, char **args)
513{
514    struct service *svc;
515    svc = service_find_by_name(args[1]);
516    if (svc) {
517        service_stop(svc);
518    }
519    return 0;
520}
521
522int do_restart(int nargs, char **args)
523{
524    struct service *svc;
525    svc = service_find_by_name(args[1]);
526    if (svc) {
527        service_stop(svc);
528        service_start(svc, NULL);
529    }
530    return 0;
531}
532
533int do_trigger(int nargs, char **args)
534{
535    action_for_each_trigger(args[1], action_add_queue_tail);
536    return 0;
537}
538
539int do_symlink(int nargs, char **args)
540{
541    return symlink(args[1], args[2]);
542}
543
544int do_rm(int nargs, char **args)
545{
546    return unlink(args[1]);
547}
548
549int do_rmdir(int nargs, char **args)
550{
551    return rmdir(args[1]);
552}
553
554int do_sysclktz(int nargs, char **args)
555{
556    struct timezone tz;
557
558    if (nargs != 2)
559        return -1;
560
561    memset(&tz, 0, sizeof(tz));
562    tz.tz_minuteswest = atoi(args[1]);
563    if (settimeofday(NULL, &tz))
564        return -1;
565    return 0;
566}
567
568int do_write(int nargs, char **args)
569{
570    const char *path = args[1];
571    const char *value = args[2];
572    char prop_val[PROP_VALUE_MAX];
573    int ret;
574
575    ret = expand_props(prop_val, value, sizeof(prop_val));
576    if (ret) {
577        ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
578        return -EINVAL;
579    }
580    return write_file(path, prop_val);
581}
582
583int do_copy(int nargs, char **args)
584{
585    char *buffer = NULL;
586    int rc = 0;
587    int fd1 = -1, fd2 = -1;
588    struct stat info;
589    int brtw, brtr;
590    char *p;
591
592    if (nargs != 3)
593        return -1;
594
595    if (stat(args[1], &info) < 0)
596        return -1;
597
598    if ((fd1 = open(args[1], O_RDONLY)) < 0)
599        goto out_err;
600
601    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
602        goto out_err;
603
604    if (!(buffer = malloc(info.st_size)))
605        goto out_err;
606
607    p = buffer;
608    brtr = info.st_size;
609    while(brtr) {
610        rc = read(fd1, p, brtr);
611        if (rc < 0)
612            goto out_err;
613        if (rc == 0)
614            break;
615        p += rc;
616        brtr -= rc;
617    }
618
619    p = buffer;
620    brtw = info.st_size;
621    while(brtw) {
622        rc = write(fd2, p, brtw);
623        if (rc < 0)
624            goto out_err;
625        if (rc == 0)
626            break;
627        p += rc;
628        brtw -= rc;
629    }
630
631    rc = 0;
632    goto out;
633out_err:
634    rc = -1;
635out:
636    if (buffer)
637        free(buffer);
638    if (fd1 >= 0)
639        close(fd1);
640    if (fd2 >= 0)
641        close(fd2);
642    return rc;
643}
644
645int do_chown(int nargs, char **args) {
646    /* GID is optional. */
647    if (nargs == 3) {
648        if (chown(args[2], decode_uid(args[1]), -1) < 0)
649            return -errno;
650    } else if (nargs == 4) {
651        if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
652            return -errno;
653    } else {
654        return -1;
655    }
656    return 0;
657}
658
659static mode_t get_mode(const char *s) {
660    mode_t mode = 0;
661    while (*s) {
662        if (*s >= '0' && *s <= '7') {
663            mode = (mode<<3) | (*s-'0');
664        } else {
665            return -1;
666        }
667        s++;
668    }
669    return mode;
670}
671
672int do_chmod(int nargs, char **args) {
673    mode_t mode = get_mode(args[1]);
674    if (chmod(args[2], mode) < 0) {
675        return -errno;
676    }
677    return 0;
678}
679
680int do_restorecon(int nargs, char **args) {
681#ifdef HAVE_SELINUX
682    char *secontext = NULL;
683    struct stat sb;
684    int i;
685
686    if (is_selinux_enabled() <= 0 || !sehandle)
687        return 0;
688
689    for (i = 1; i < nargs; i++) {
690        if (lstat(args[i], &sb) < 0)
691            return -errno;
692        if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0)
693            return -errno;
694        if (lsetfilecon(args[i], secontext) < 0) {
695            freecon(secontext);
696            return -errno;
697        }
698        freecon(secontext);
699    }
700#endif
701    return 0;
702}
703
704int do_setsebool(int nargs, char **args) {
705#ifdef HAVE_SELINUX
706    SELboolean *b = alloca(nargs * sizeof(SELboolean));
707    char *v;
708    int i;
709
710    if (is_selinux_enabled() <= 0)
711        return 0;
712
713    for (i = 1; i < nargs; i++) {
714        char *name = args[i];
715        v = strchr(name, '=');
716        if (!v) {
717            ERROR("setsebool: argument %s had no =\n", name);
718            return -EINVAL;
719        }
720        *v++ = 0;
721        b[i-1].name = name;
722        if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
723            b[i-1].value = 1;
724        else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
725            b[i-1].value = 0;
726        else {
727            ERROR("setsebool: invalid value %s\n", v);
728            return -EINVAL;
729        }
730    }
731
732    if (security_set_boolean_list(nargs - 1, b, 0) < 0)
733        return -errno;
734#endif
735    return 0;
736}
737
738int do_loglevel(int nargs, char **args) {
739    if (nargs == 2) {
740        klog_set_level(atoi(args[1]));
741        return 0;
742    }
743    return -1;
744}
745
746int do_load_persist_props(int nargs, char **args) {
747    if (nargs == 1) {
748        load_persist_props();
749        return 0;
750    }
751    return -1;
752}
753
754int do_wait(int nargs, char **args)
755{
756    if (nargs == 2) {
757        return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
758    }
759    return -1;
760}
761