builtins.c revision e520d036165b36cf5c4cb305f9cec7d183977b61
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 -1;
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 -1;
63    } else {
64        return 0;
65    }
66}
67
68static int insmod(const char *filename)
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, "");
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);
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
176int do_insmod(int nargs, char **args)
177{
178    return insmod(args[1]);
179}
180
181int do_import(int nargs, char **args)
182{
183    return -1;
184}
185
186int do_mkdir(int nargs, char **args)
187{
188    mode_t mode = 0755;
189
190    /* mkdir <path> [mode] [owner] [group] */
191
192    if (nargs >= 3) {
193        mode = strtoul(args[2], 0, 8);
194    }
195
196    if (mkdir(args[1], mode)) {
197        return -errno;
198    }
199
200    if (nargs >= 4) {
201        uid_t uid = decode_uid(args[3]);
202        gid_t gid = -1;
203
204        if (nargs == 5) {
205            gid = decode_uid(args[4]);
206        }
207
208        if (chown(args[1], uid, gid)) {
209            return -errno;
210        }
211    }
212
213    return 0;
214}
215
216static struct {
217    const char *name;
218    unsigned flag;
219} mount_flags[] = {
220    { "noatime",    MS_NOATIME },
221    { "nosuid",     MS_NOSUID },
222    { "nodev",      MS_NODEV },
223    { "nodiratime", MS_NODIRATIME },
224    { "ro",         MS_RDONLY },
225    { "rw",         0 },
226    { "remount",    MS_REMOUNT },
227    { "defaults",   0 },
228    { 0,            0 },
229};
230
231/* mount <type> <device> <path> <flags ...> <options> */
232int do_mount(int nargs, char **args)
233{
234    char tmp[64];
235    char *source, *target, *system;
236    char *options = NULL;
237    unsigned flags = 0;
238    int n, i;
239
240    for (n = 4; n < nargs; n++) {
241        for (i = 0; mount_flags[i].name; i++) {
242            if (!strcmp(args[n], mount_flags[i].name)) {
243                flags |= mount_flags[i].flag;
244                break;
245            }
246        }
247
248        /* if our last argument isn't a flag, wolf it up as an option string */
249        if (n + 1 == nargs && !mount_flags[i].name)
250            options = args[n];
251    }
252
253    system = args[1];
254    source = args[2];
255    target = args[3];
256
257    if (!strncmp(source, "mtd@", 4)) {
258        n = mtd_name_to_number(source + 4);
259        if (n < 0) {
260            return -1;
261        }
262
263        sprintf(tmp, "/dev/block/mtdblock%d", n);
264
265        if (mount(tmp, target, system, flags, options) < 0) {
266            return -1;
267        }
268
269        return 0;
270    } else if (!strncmp(source, "loop@", 5)) {
271        int mode, loop, fd;
272        struct loop_info info;
273
274        mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
275        fd = open(source + 5, mode);
276        if (fd < 0) {
277            return -1;
278        }
279
280        for (n = 0; ; n++) {
281            sprintf(tmp, "/dev/block/loop%d", n);
282            loop = open(tmp, mode);
283            if (loop < 0) {
284                return -1;
285            }
286
287            /* if it is a blank loop device */
288            if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
289                /* if it becomes our loop device */
290                if (ioctl(loop, LOOP_SET_FD, fd) >= 0) {
291                    close(fd);
292
293                    if (mount(tmp, target, system, flags, options) < 0) {
294                        ioctl(loop, LOOP_CLR_FD, 0);
295                        close(loop);
296                        return -1;
297                    }
298
299                    close(loop);
300                    return 0;
301                }
302            }
303
304            close(loop);
305        }
306
307        close(fd);
308        ERROR("out of loopback devices");
309        return -1;
310    } else {
311        if (mount(source, target, system, flags, options) < 0) {
312            return -1;
313        }
314
315        return 0;
316    }
317}
318
319int do_setkey(int nargs, char **args)
320{
321    struct kbentry kbe;
322    kbe.kb_table = strtoul(args[1], 0, 0);
323    kbe.kb_index = strtoul(args[2], 0, 0);
324    kbe.kb_value = strtoul(args[3], 0, 0);
325    return setkey(&kbe);
326}
327
328int do_setprop(int nargs, char **args)
329{
330    property_set(args[1], args[2]);
331    return 0;
332}
333
334int do_setrlimit(int nargs, char **args)
335{
336    struct rlimit limit;
337    int resource;
338    resource = atoi(args[1]);
339    limit.rlim_cur = atoi(args[2]);
340    limit.rlim_max = atoi(args[3]);
341    return setrlimit(resource, &limit);
342}
343
344int do_start(int nargs, char **args)
345{
346    struct service *svc;
347    svc = service_find_by_name(args[1]);
348    if (svc) {
349        service_start(svc);
350    }
351    return 0;
352}
353
354int do_stop(int nargs, char **args)
355{
356    struct service *svc;
357    svc = service_find_by_name(args[1]);
358    if (svc) {
359        service_stop(svc);
360    }
361    return 0;
362}
363
364int do_restart(int nargs, char **args)
365{
366    struct service *svc;
367    svc = service_find_by_name(args[1]);
368    if (svc) {
369        service_stop(svc);
370        service_start(svc);
371    }
372    return 0;
373}
374
375int do_trigger(int nargs, char **args)
376{
377    return 0;
378}
379
380int do_symlink(int nargs, char **args)
381{
382    return symlink(args[1], args[2]);
383}
384
385int do_write(int nargs, char **args)
386{
387    return write_file(args[1], args[2]);
388}
389
390int do_chown(int nargs, char **args) {
391    /* GID is optional. */
392    if (nargs == 3) {
393        if (chown(args[2], decode_uid(args[1]), -1) < 0)
394            return -errno;
395    } else if (nargs == 4) {
396        if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
397            return -errno;
398    } else {
399        return -1;
400    }
401    return 0;
402}
403
404static mode_t get_mode(const char *s) {
405    mode_t mode = 0;
406    while (*s) {
407        if (*s >= '0' && *s <= '7') {
408            mode = (mode<<3) | (*s-'0');
409        } else {
410            return -1;
411        }
412        s++;
413    }
414    return mode;
415}
416
417int do_chmod(int nargs, char **args) {
418    mode_t mode = get_mode(args[1]);
419    if (chmod(args[2], mode) < 0) {
420        return -errno;
421    }
422    return 0;
423}
424
425int do_loglevel(int nargs, char **args) {
426    if (nargs == 2) {
427        log_set_level(atoi(args[1]));
428        return 0;
429    }
430    return -1;
431}
432
433int do_device(int nargs, char **args) {
434    int len;
435    char tmp[64];
436    char *source = args[1];
437    int prefix = 0;
438
439    if (nargs != 5)
440        return -1;
441    /* Check for wildcard '*' at the end which indicates a prefix. */
442    len = strlen(args[1]) - 1;
443    if (args[1][len] == '*') {
444        args[1][len] = '\0';
445        prefix = 1;
446    }
447    /* If path starts with mtd@ lookup the mount number. */
448    if (!strncmp(source, "mtd@", 4)) {
449        int n = mtd_name_to_number(source + 4);
450        if (n >= 0) {
451            snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n);
452            source = tmp;
453        }
454    }
455    add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]),
456                          decode_uid(args[4]), prefix);
457    return 0;
458}
459