diff options
Diffstat (limited to 'init/builtins.c')
-rw-r--r-- | init/builtins.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/init/builtins.c b/init/builtins.c new file mode 100644 index 0000000..ba34410 --- /dev/null +++ b/init/builtins.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <linux/kd.h> +#include <errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/if.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <sys/mount.h> +#include <sys/resource.h> + +#include "init.h" +#include "keywords.h" +#include "property_service.h" +#include "devices.h" + +#include <private/android_filesystem_config.h> + +void add_environment(const char *name, const char *value); + +extern int init_module(void *, unsigned long, const char *); + +static int write_file(const char *path, const char *value) +{ + int fd, ret, len; + + fd = open(path, O_WRONLY|O_CREAT, 0622); + + if (fd < 0) + return -1; + + len = strlen(value); + + do { + ret = write(fd, value, len); + } while (ret < 0 && errno == EINTR); + + close(fd); + if (ret < 0) { + return -1; + } else { + return 0; + } +} + +static int insmod(const char *filename) +{ + void *module; + unsigned size; + int ret; + + module = read_file(filename, &size); + if (!module) + return -1; + + ret = init_module(module, size, ""); + + free(module); + + return ret; +} + +static int setkey(struct kbentry *kbe) +{ + int fd, ret; + + fd = open("/dev/tty0", O_RDWR | O_SYNC); + if (fd < 0) + return -1; + + ret = ioctl(fd, KDSKBENT, kbe); + + close(fd); + return ret; +} + +static int __ifupdown(const char *interface, int up) +{ + struct ifreq ifr; + int s, ret; + + strlcpy(ifr.ifr_name, interface, IFNAMSIZ); + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return -1; + + ret = ioctl(s, SIOCGIFFLAGS, &ifr); + if (ret < 0) { + goto done; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + ret = ioctl(s, SIOCSIFFLAGS, &ifr); + +done: + close(s); + return ret; +} + +static void service_start_if_not_disabled(struct service *svc) +{ + if (!(svc->flags & SVC_DISABLED)) { + service_start(svc); + } +} + +int do_class_start(int nargs, char **args) +{ + /* Starting a class does not start services + * which are explicitly disabled. They must + * be started individually. + */ + service_for_each_class(args[1], service_start_if_not_disabled); + return 0; +} + +int do_class_stop(int nargs, char **args) +{ + service_for_each_class(args[1], service_stop); + return 0; +} + +int do_domainname(int nargs, char **args) +{ + return write_file("/proc/sys/kernel/domainname", args[1]); +} + +int do_exec(int nargs, char **args) +{ + return -1; +} + +int do_export(int nargs, char **args) +{ + add_environment(args[1], args[2]); + return 0; +} + +int do_hostname(int nargs, char **args) +{ + return write_file("/proc/sys/kernel/hostname", args[1]); +} + +int do_ifup(int nargs, char **args) +{ + return __ifupdown(args[1], 1); +} + +int do_insmod(int nargs, char **args) +{ + return insmod(args[1]); +} + +int do_import(int nargs, char **args) +{ + return -1; +} + +int do_mkdir(int nargs, char **args) +{ + mode_t mode = 0755; + + /* mkdir <path> [mode] [owner] [group] */ + + if (nargs >= 3) { + mode = strtoul(args[2], 0, 8); + } + + if (mkdir(args[1], mode)) { + return -errno; + } + + if (nargs >= 4) { + uid_t uid = decode_uid(args[3]); + gid_t gid = -1; + + if (nargs == 5) { + gid = decode_uid(args[4]); + } + + if (chown(args[1], uid, gid)) { + return -errno; + } + } + + return 0; +} + +static struct { + const char *name; + unsigned flag; +} mount_flags[] = { + { "noatime", MS_NOATIME }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "defaults", 0 }, + { 0, 0 }, +}; + +/* mount <type> <device> <path> <flags ...> <options> */ +int do_mount(int nargs, char **args) +{ + char tmp[64]; + char *source; + char *options = NULL; + unsigned flags = 0; + int n, i; + + for (n = 4; n < nargs; n++) { + for (i = 0; mount_flags[i].name; i++) { + if (!strcmp(args[n], mount_flags[i].name)) { + flags |= mount_flags[i].flag; + break; + } + } + + /* if our last argument isn't a flag, wolf it up as an option string */ + if (n + 1 == nargs && !mount_flags[i].name) + options = args[n]; + } + + source = args[2]; + if (!strncmp(source, "mtd@", 4)) { + n = mtd_name_to_number(source + 4); + if (n >= 0) { + sprintf(tmp, "/dev/block/mtdblock%d", n); + source = tmp; + } + } + return mount(source, args[3], args[1], flags, options); +} + +int do_setkey(int nargs, char **args) +{ + struct kbentry kbe; + kbe.kb_table = strtoul(args[1], 0, 0); + kbe.kb_index = strtoul(args[2], 0, 0); + kbe.kb_value = strtoul(args[3], 0, 0); + return setkey(&kbe); +} + +int do_setprop(int nargs, char **args) +{ + property_set(args[1], args[2]); + return 0; +} + +int do_setrlimit(int nargs, char **args) +{ + struct rlimit limit; + int resource; + resource = atoi(args[1]); + limit.rlim_cur = atoi(args[2]); + limit.rlim_max = atoi(args[3]); + return setrlimit(resource, &limit); +} + +int do_start(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + service_start(svc); + } + return 0; +} + +int do_stop(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + service_stop(svc); + } + return 0; +} + +int do_restart(int nargs, char **args) +{ + struct service *svc; + svc = service_find_by_name(args[1]); + if (svc) { + service_stop(svc); + service_start(svc); + } + return 0; +} + +int do_trigger(int nargs, char **args) +{ + return 0; +} + +int do_symlink(int nargs, char **args) +{ + return symlink(args[1], args[2]); +} + +int do_write(int nargs, char **args) +{ + return write_file(args[1], args[2]); +} + +int do_chown(int nargs, char **args) { + /* GID is optional. */ + if (nargs == 3) { + if (chown(args[2], decode_uid(args[1]), -1) < 0) + return -errno; + } else if (nargs == 4) { + if (chown(args[3], decode_uid(args[1]), decode_uid(args[2]))) + return -errno; + } else { + return -1; + } + return 0; +} + +static mode_t get_mode(const char *s) { + mode_t mode = 0; + while (*s) { + if (*s >= '0' && *s <= '7') { + mode = (mode<<3) | (*s-'0'); + } else { + return -1; + } + s++; + } + return mode; +} + +int do_chmod(int nargs, char **args) { + mode_t mode = get_mode(args[1]); + if (chmod(args[2], mode) < 0) { + return -errno; + } + return 0; +} + +int do_loglevel(int nargs, char **args) { + if (nargs == 2) { + log_set_level(atoi(args[1])); + return 0; + } + return -1; +} + +int do_device(int nargs, char **args) { + int len; + char tmp[64]; + char *source = args[1]; + int prefix = 0; + + if (nargs != 5) + return -1; + /* Check for wildcard '*' at the end which indicates a prefix. */ + len = strlen(args[1]) - 1; + if (args[1][len] == '*') { + args[1][len] = '\0'; + prefix = 1; + } + /* If path starts with mtd@ lookup the mount number. */ + if (!strncmp(source, "mtd@", 4)) { + int n = mtd_name_to_number(source + 4); + if (n >= 0) { + snprintf(tmp, sizeof(tmp), "/dev/mtd/mtd%d", n); + source = tmp; + } + } + add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]), + decode_uid(args[4]), prefix); + return 0; +} |