builtins.c revision 008abac082f1c098d402f944d9287dce67ffce0a
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_class_start(int nargs, char **args)
135{
136        /* Starting a class does not start services
137         * which are explicitly disabled.  They must
138         * be started individually.
139         */
140    service_for_each_class(args[1], service_start_if_not_disabled);
141    return 0;
142}
143
144int do_class_stop(int nargs, char **args)
145{
146    service_for_each_class(args[1], service_stop);
147    return 0;
148}
149
150int do_domainname(int nargs, char **args)
151{
152    return write_file("/proc/sys/kernel/domainname", args[1]);
153}
154
155int do_exec(int nargs, char **args)
156{
157    return -1;
158}
159
160int do_export(int nargs, char **args)
161{
162    add_environment(args[1], args[2]);
163    return 0;
164}
165
166int do_hostname(int nargs, char **args)
167{
168    return write_file("/proc/sys/kernel/hostname", args[1]);
169}
170
171int do_ifup(int nargs, char **args)
172{
173    return __ifupdown(args[1], 1);
174}
175
176
177static int do_insmod_inner(int nargs, char **args, int opt_len)
178{
179    char options[opt_len + 1];
180    int i;
181
182    options[0] = '\0';
183    if (nargs > 2) {
184        strcpy(options, args[2]);
185        for (i = 3; i < nargs; ++i) {
186            strcat(options, " ");
187            strcat(options, args[i]);
188        }
189    }
190
191    return insmod(args[1], options);
192}
193
194int do_insmod(int nargs, char **args)
195{
196    int i;
197    int size = 0;
198
199    if (nargs > 2) {
200        for (i = 2; i < nargs; ++i)
201            size += strlen(args[i]) + 1;
202    }
203
204    return do_insmod_inner(nargs, args, size);
205}
206
207int do_import(int nargs, char **args)
208{
209    return -1;
210}
211
212int do_mkdir(int nargs, char **args)
213{
214    mode_t mode = 0755;
215
216    /* mkdir <path> [mode] [owner] [group] */
217
218    if (nargs >= 3) {
219        mode = strtoul(args[2], 0, 8);
220    }
221
222    if (mkdir(args[1], mode)) {
223        return -errno;
224    }
225
226    if (nargs >= 4) {
227        uid_t uid = decode_uid(args[3]);
228        gid_t gid = -1;
229
230        if (nargs == 5) {
231            gid = decode_uid(args[4]);
232        }
233
234        if (chown(args[1], uid, gid)) {
235            return -errno;
236        }
237    }
238
239    return 0;
240}
241
242static struct {
243    const char *name;
244    unsigned flag;
245} mount_flags[] = {
246    { "noatime",    MS_NOATIME },
247    { "nosuid",     MS_NOSUID },
248    { "nodev",      MS_NODEV },
249    { "nodiratime", MS_NODIRATIME },
250    { "ro",         MS_RDONLY },
251    { "rw",         0 },
252    { "remount",    MS_REMOUNT },
253    { "defaults",   0 },
254    { 0,            0 },
255};
256
257/* mount <type> <device> <path> <flags ...> <options> */
258int do_mount(int nargs, char **args)
259{
260    char tmp[64];
261    char *source, *target, *system;
262    char *options = NULL;
263    unsigned flags = 0;
264    int n, i;
265
266    for (n = 4; n < nargs; n++) {
267        for (i = 0; mount_flags[i].name; i++) {
268            if (!strcmp(args[n], mount_flags[i].name)) {
269                flags |= mount_flags[i].flag;
270                break;
271            }
272        }
273
274        /* if our last argument isn't a flag, wolf it up as an option string */
275        if (n + 1 == nargs && !mount_flags[i].name)
276            options = args[n];
277    }
278
279    system = args[1];
280    source = args[2];
281    target = args[3];
282
283    if (!strncmp(source, "mtd@", 4)) {
284        n = mtd_name_to_number(source + 4);
285        if (n < 0) {
286            return -1;
287        }
288
289        sprintf(tmp, "/dev/block/mtdblock%d", n);
290
291        if (mount(tmp, target, system, flags, options) < 0) {
292            return -1;
293        }
294
295        return 0;
296    } else if (!strncmp(source, "loop@", 5)) {
297        int mode, loop, fd;
298        struct loop_info info;
299
300        mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
301        fd = open(source + 5, mode);
302        if (fd < 0) {
303            return -1;
304        }
305
306        for (n = 0; ; n++) {
307            sprintf(tmp, "/dev/block/loop%d", n);
308            loop = open(tmp, mode);
309            if (loop < 0) {
310                return -1;
311            }
312
313            /* if it is a blank loop device */
314            if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
315                /* if it becomes our loop device */
316                if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
317                    close(fd);
318
319                    if (mount(tmp, target, system, flags, options) < 0) {
320                        ioctl(loop, LOOP_CLR_FD, 0);
321                        close(loop);
322                        return -1;
323                    }
324
325                    close(loop);
326                    return 0;
327                }
328            }
329
330            close(loop);
331        }
332
333        close(fd);
334        ERROR("out of loopback devices");
335        return -1;
336    } else {
337        if (mount(source, target, system, flags, options) < 0) {
338            return -1;
339        }
340
341        return 0;
342    }
343}
344
345int do_setkey(int nargs, char **args)
346{
347    struct kbentry kbe;
348    kbe.kb_table = strtoul(args[1], 0, 0);
349    kbe.kb_index = strtoul(args[2], 0, 0);
350    kbe.kb_value = strtoul(args[3], 0, 0);
351    return setkey(&kbe);
352}
353
354int do_setprop(int nargs, char **args)
355{
356    property_set(args[1], args[2]);
357    return 0;
358}
359
360int do_setrlimit(int nargs, char **args)
361{
362    struct rlimit limit;
363    int resource;
364    resource = atoi(args[1]);
365    limit.rlim_cur = atoi(args[2]);
366    limit.rlim_max = atoi(args[3]);
367    return setrlimit(resource, &limit);
368}
369
370int do_start(int nargs, char **args)
371{
372    struct service *svc;
373    svc = service_find_by_name(args[1]);
374    if (svc) {
375        service_start(svc, NULL);
376    }
377    return 0;
378}
379
380int do_stop(int nargs, char **args)
381{
382    struct service *svc;
383    svc = service_find_by_name(args[1]);
384    if (svc) {
385        service_stop(svc);
386    }
387    return 0;
388}
389
390int do_restart(int nargs, char **args)
391{
392    struct service *svc;
393    svc = service_find_by_name(args[1]);
394    if (svc) {
395        service_stop(svc);
396        service_start(svc, NULL);
397    }
398    return 0;
399}
400
401int do_trigger(int nargs, char **args)
402{
403    return 0;
404}
405
406int do_symlink(int nargs, char **args)
407{
408    return symlink(args[1], args[2]);
409}
410
411int do_sysclktz(int nargs, char **args)
412{
413    struct timezone tz;
414
415    if (nargs != 2)
416        return -1;
417
418    memset(&tz, 0, sizeof(tz));
419    tz.tz_minuteswest = atoi(args[1]);
420    if (settimeofday(NULL, &tz))
421        return -1;
422    return 0;
423}
424
425int do_write(int nargs, char **args)
426{
427    return write_file(args[1], args[2]);
428}
429
430int do_chown(int nargs, char **args) {
431    /* GID is optional. */
432    if (nargs == 3) {
433        if (chown(args[2], decode_uid(args[1]), -1) < 0)
434            return -errno;
435    } else if (nargs == 4) {
436        if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
437            return -errno;
438    } else {
439        return -1;
440    }
441    return 0;
442}
443
444static mode_t get_mode(const char *s) {
445    mode_t mode = 0;
446    while (*s) {
447        if (*s >= '0' && *s <= '7') {
448            mode = (mode<<3) | (*s-'0');
449        } else {
450            return -1;
451        }
452        s++;
453    }
454    return mode;
455}
456
457int do_chmod(int nargs, char **args) {
458    mode_t mode = get_mode(args[1]);
459    if (chmod(args[2], mode) < 0) {
460        return -errno;
461    }
462    return 0;
463}
464
465int do_loglevel(int nargs, char **args) {
466    if (nargs == 2) {
467        log_set_level(atoi(args[1]));
468        return 0;
469    }
470    return -1;
471}
472
473int do_device(int nargs, char **args) {
474    int len;
475    char tmp[64];
476    char *source = args[1];
477    int prefix = 0;
478
479    if (nargs != 5)
480        return -1;
481    /* Check for wildcard '*' at the end which indicates a prefix. */
482    len = strlen(args[1]) - 1;
483    if (args[1][len] == '*') {
484        args[1][len] = '\0';
485        prefix = 1;
486    }
487    /* If path starts with mtd@ lookup the mount number. */
488    if (!strncmp(source, "mtd@", 4)) {
489        int n = mtd_name_to_number(source + 4);
490        if (n >= 0) {
491            snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n);
492            source = tmp;
493        }
494    }
495    add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]),
496                          decode_uid(args[4]), prefix);
497    return 0;
498}
499