builtins.c revision 4d0b21f4ac5908f695c9c0759c3a34511d8fd97b
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
34#include "init.h"
35#include "keywords.h"
36#include "property_service.h"
37#include "devices.h"
38
39#include <private/android_filesystem_config.h>
40
41void add_environment(const char *name, const char *value);
42
43extern int init_module(void *, unsigned long, const char *);
44
45static int write_file(const char *path, const char *value)
46{
47    int fd, ret, len;
48
49    fd = open(path, O_WRONLY|O_CREAT, 0622);
50
51    if (fd < 0)
52        return -errno;
53
54    len = strlen(value);
55
56    do {
57        ret = write(fd, value, len);
58    } while (ret < 0 && errno == EINTR);
59
60    close(fd);
61    if (ret < 0) {
62        return -errno;
63    } else {
64        return 0;
65    }
66}
67
68static int insmod(const char *filename, char *options)
69{
70    void *module;
71    unsigned size;
72    int ret;
73
74    module = read_file(filename, &size);
75    if (!module)
76        return -1;
77
78    ret = init_module(module, size, options);
79
80    free(module);
81
82    return ret;
83}
84
85static int setkey(struct kbentry *kbe)
86{
87    int fd, ret;
88
89    fd = open("/dev/tty0", O_RDWR | O_SYNC);
90    if (fd < 0)
91        return -1;
92
93    ret = ioctl(fd, KDSKBENT, kbe);
94
95    close(fd);
96    return ret;
97}
98
99static int __ifupdown(const char *interface, int up)
100{
101    struct ifreq ifr;
102    int s, ret;
103
104    strlcpy(ifr.ifr_name, interface, IFNAMSIZ);
105
106    s = socket(AF_INET, SOCK_DGRAM, 0);
107    if (s < 0)
108        return -1;
109
110    ret = ioctl(s, SIOCGIFFLAGS, &ifr);
111    if (ret < 0) {
112        goto done;
113    }
114
115    if (up)
116        ifr.ifr_flags |= IFF_UP;
117    else
118        ifr.ifr_flags &= ~IFF_UP;
119
120    ret = ioctl(s, SIOCSIFFLAGS, &ifr);
121
122done:
123    close(s);
124    return ret;
125}
126
127static void service_start_if_not_disabled(struct service *svc)
128{
129    if (!(svc->flags & SVC_DISABLED)) {
130        service_start(svc, NULL);
131    }
132}
133
134int do_chdir(int nargs, char **args)
135{
136    chdir(args[1]);
137    return 0;
138}
139
140int do_chroot(int nargs, char **args)
141{
142    chroot(args[1]);
143    return 0;
144}
145
146int do_class_start(int nargs, char **args)
147{
148        /* Starting a class does not start services
149         * which are explicitly disabled.  They must
150         * be started individually.
151         */
152    service_for_each_class(args[1], service_start_if_not_disabled);
153    return 0;
154}
155
156int do_class_stop(int nargs, char **args)
157{
158    service_for_each_class(args[1], service_stop);
159    return 0;
160}
161
162int do_domainname(int nargs, char **args)
163{
164    return write_file("/proc/sys/kernel/domainname", args[1]);
165}
166
167int do_exec(int nargs, char **args)
168{
169    return -1;
170}
171
172int do_export(int nargs, char **args)
173{
174    add_environment(args[1], args[2]);
175    return 0;
176}
177
178int do_hostname(int nargs, char **args)
179{
180    return write_file("/proc/sys/kernel/hostname", args[1]);
181}
182
183int do_ifup(int nargs, char **args)
184{
185    return __ifupdown(args[1], 1);
186}
187
188
189static int do_insmod_inner(int nargs, char **args, int opt_len)
190{
191    char options[opt_len + 1];
192    int i;
193
194    options[0] = '\0';
195    if (nargs > 2) {
196        strcpy(options, args[2]);
197        for (i = 3; i < nargs; ++i) {
198            strcat(options, " ");
199            strcat(options, args[i]);
200        }
201    }
202
203    return insmod(args[1], options);
204}
205
206int do_insmod(int nargs, char **args)
207{
208    int i;
209    int size = 0;
210
211    if (nargs > 2) {
212        for (i = 2; i < nargs; ++i)
213            size += strlen(args[i]) + 1;
214    }
215
216    return do_insmod_inner(nargs, args, size);
217}
218
219int do_import(int nargs, char **args)
220{
221    return parse_config_file(args[1]);
222}
223
224int do_mkdir(int nargs, char **args)
225{
226    mode_t mode = 0755;
227
228    /* mkdir <path> [mode] [owner] [group] */
229
230    if (nargs >= 3) {
231        mode = strtoul(args[2], 0, 8);
232    }
233
234    if (mkdir(args[1], mode)) {
235        return -errno;
236    }
237
238    if (nargs >= 4) {
239        uid_t uid = decode_uid(args[3]);
240        gid_t gid = -1;
241
242        if (nargs == 5) {
243            gid = decode_uid(args[4]);
244        }
245
246        if (chown(args[1], uid, gid)) {
247            return -errno;
248        }
249    }
250
251    return 0;
252}
253
254static struct {
255    const char *name;
256    unsigned flag;
257} mount_flags[] = {
258    { "noatime",    MS_NOATIME },
259    { "nosuid",     MS_NOSUID },
260    { "nodev",      MS_NODEV },
261    { "nodiratime", MS_NODIRATIME },
262    { "ro",         MS_RDONLY },
263    { "rw",         0 },
264    { "remount",    MS_REMOUNT },
265    { "defaults",   0 },
266    { 0,            0 },
267};
268
269/* mount <type> <device> <path> <flags ...> <options> */
270int do_mount(int nargs, char **args)
271{
272    char tmp[64];
273    char *source, *target, *system;
274    char *options = NULL;
275    unsigned flags = 0;
276    int n, i;
277
278    for (n = 4; n < nargs; n++) {
279        for (i = 0; mount_flags[i].name; i++) {
280            if (!strcmp(args[n], mount_flags[i].name)) {
281                flags |= mount_flags[i].flag;
282                break;
283            }
284        }
285
286        /* if our last argument isn't a flag, wolf it up as an option string */
287        if (n + 1 == nargs && !mount_flags[i].name)
288            options = args[n];
289    }
290
291    system = args[1];
292    source = args[2];
293    target = args[3];
294
295    if (!strncmp(source, "mtd@", 4)) {
296        n = mtd_name_to_number(source + 4);
297        if (n < 0) {
298            return -1;
299        }
300
301        sprintf(tmp, "/dev/block/mtdblock%d", n);
302
303        if (mount(tmp, target, system, flags, options) < 0) {
304            return -1;
305        }
306
307        return 0;
308    } else if (!strncmp(source, "loop@", 5)) {
309        int mode, loop, fd;
310        struct loop_info info;
311
312        mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
313        fd = open(source + 5, mode);
314        if (fd < 0) {
315            return -1;
316        }
317
318        for (n = 0; ; n++) {
319            sprintf(tmp, "/dev/block/loop%d", n);
320            loop = open(tmp, mode);
321            if (loop < 0) {
322                return -1;
323            }
324
325            /* if it is a blank loop device */
326            if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
327                /* if it becomes our loop device */
328                if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
329                    close(fd);
330
331                    if (mount(tmp, target, system, flags, options) < 0) {
332                        ioctl(loop, LOOP_CLR_FD, 0);
333                        close(loop);
334                        return -1;
335                    }
336
337                    close(loop);
338                    return 0;
339                }
340            }
341
342            close(loop);
343        }
344
345        close(fd);
346        ERROR("out of loopback devices");
347        return -1;
348    } else {
349        if (mount(source, target, system, flags, options) < 0) {
350            return -1;
351        }
352
353        return 0;
354    }
355}
356
357int do_setkey(int nargs, char **args)
358{
359    struct kbentry kbe;
360    kbe.kb_table = strtoul(args[1], 0, 0);
361    kbe.kb_index = strtoul(args[2], 0, 0);
362    kbe.kb_value = strtoul(args[3], 0, 0);
363    return setkey(&kbe);
364}
365
366int do_setprop(int nargs, char **args)
367{
368    property_set(args[1], args[2]);
369    return 0;
370}
371
372int do_setrlimit(int nargs, char **args)
373{
374    struct rlimit limit;
375    int resource;
376    resource = atoi(args[1]);
377    limit.rlim_cur = atoi(args[2]);
378    limit.rlim_max = atoi(args[3]);
379    return setrlimit(resource, &limit);
380}
381
382int do_start(int nargs, char **args)
383{
384    struct service *svc;
385    svc = service_find_by_name(args[1]);
386    if (svc) {
387        service_start(svc, NULL);
388    }
389    return 0;
390}
391
392int do_stop(int nargs, char **args)
393{
394    struct service *svc;
395    svc = service_find_by_name(args[1]);
396    if (svc) {
397        service_stop(svc);
398    }
399    return 0;
400}
401
402int do_restart(int nargs, char **args)
403{
404    struct service *svc;
405    svc = service_find_by_name(args[1]);
406    if (svc) {
407        service_stop(svc);
408        service_start(svc, NULL);
409    }
410    return 0;
411}
412
413int do_trigger(int nargs, char **args)
414{
415    action_for_each_trigger(args[1], action_add_queue_tail);
416    drain_action_queue();
417    return 0;
418}
419
420int do_symlink(int nargs, char **args)
421{
422    return symlink(args[1], args[2]);
423}
424
425int do_sysclktz(int nargs, char **args)
426{
427    struct timezone tz;
428
429    if (nargs != 2)
430        return -1;
431
432    memset(&tz, 0, sizeof(tz));
433    tz.tz_minuteswest = atoi(args[1]);
434    if (settimeofday(NULL, &tz))
435        return -1;
436    return 0;
437}
438
439int do_write(int nargs, char **args)
440{
441    return write_file(args[1], args[2]);
442}
443
444int do_copy(int nargs, char **args)
445{
446    char *buffer = NULL;
447    int rc = 0;
448    int fd1 = -1, fd2 = -1;
449    struct stat info;
450    int brtw, brtr;
451    char *p;
452
453    if (nargs != 3)
454        return -1;
455
456    if (stat(args[1], &info) < 0)
457        return -1;
458
459    if ((fd1 = open(args[1], O_RDONLY)) < 0)
460        goto out_err;
461
462    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC, 0660)) < 0)
463        goto out_err;
464
465    if (!(buffer = malloc(info.st_size)))
466        goto out_err;
467
468    p = buffer;
469    brtr = info.st_size;
470    while(brtr) {
471        rc = read(fd1, p, brtr);
472        if (rc < 0)
473            goto out_err;
474        if (rc == 0)
475            break;
476        p += rc;
477        brtr -= rc;
478    }
479
480    p = buffer;
481    brtw = info.st_size;
482    while(brtw) {
483        rc = write(fd2, p, brtw);
484        if (rc < 0)
485            goto out_err;
486        if (rc == 0)
487            break;
488        p += rc;
489        brtw -= rc;
490    }
491
492    rc = 0;
493    goto out;
494out_err:
495    rc = -1;
496out:
497    if (buffer)
498        free(buffer);
499    if (fd1 >= 0)
500        close(fd1);
501    if (fd2 >= 0)
502        close(fd2);
503    return rc;
504}
505
506int do_chown(int nargs, char **args) {
507    /* GID is optional. */
508    if (nargs == 3) {
509        if (chown(args[2], decode_uid(args[1]), -1) < 0)
510            return -errno;
511    } else if (nargs == 4) {
512        if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
513            return -errno;
514    } else {
515        return -1;
516    }
517    return 0;
518}
519
520static mode_t get_mode(const char *s) {
521    mode_t mode = 0;
522    while (*s) {
523        if (*s >= '0' && *s <= '7') {
524            mode = (mode<<3) | (*s-'0');
525        } else {
526            return -1;
527        }
528        s++;
529    }
530    return mode;
531}
532
533int do_chmod(int nargs, char **args) {
534    mode_t mode = get_mode(args[1]);
535    if (chmod(args[2], mode) < 0) {
536        return -errno;
537    }
538    return 0;
539}
540
541int do_loglevel(int nargs, char **args) {
542    if (nargs == 2) {
543        log_set_level(atoi(args[1]));
544        return 0;
545    }
546    return -1;
547}
548
549int do_device(int nargs, char **args) {
550    int len;
551    char tmp[64];
552    char *source = args[1];
553    int prefix = 0;
554
555    if (nargs != 5)
556        return -1;
557    /* Check for wildcard '*' at the end which indicates a prefix. */
558    len = strlen(args[1]) - 1;
559    if (args[1][len] == '*') {
560        args[1][len] = '\0';
561        prefix = 1;
562    }
563    /* If path starts with mtd@ lookup the mount number. */
564    if (!strncmp(source, "mtd@", 4)) {
565        int n = mtd_name_to_number(source + 4);
566        if (n >= 0) {
567            snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n);
568            source = tmp;
569        }
570    }
571    add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]),
572                          decode_uid(args[4]), prefix);
573    return 0;
574}
575