diff options
Diffstat (limited to 'init')
| -rw-r--r-- | init/Android.mk | 26 | ||||
| -rw-r--r-- | init/builtins.c | 261 | ||||
| -rw-r--r-- | init/devices.c | 235 | ||||
| -rwxr-xr-x | init/init.c | 272 | ||||
| -rw-r--r-- | init/init.h | 6 | ||||
| -rw-r--r-- | init/init_parser.c | 26 | ||||
| -rw-r--r-- | init/keychords.c | 11 | ||||
| -rw-r--r-- | init/keywords.h | 4 | ||||
| -rwxr-xr-x[-rw-r--r--] | init/property_service.c | 245 | ||||
| -rw-r--r-- | init/property_service.h | 19 | ||||
| -rw-r--r-- | init/readme.txt | 31 | ||||
| -rw-r--r-- | init/signal_handler.c | 4 | ||||
| -rw-r--r-- | init/ueventd.c | 19 | ||||
| -rwxr-xr-x | init/util.c | 70 | ||||
| -rw-r--r-- | init/util.h | 2 | ||||
| -rw-r--r-- | init/watchdogd.c | 77 | ||||
| -rw-r--r-- | init/watchdogd.h | 22 |
17 files changed, 809 insertions, 521 deletions
diff --git a/init/Android.mk b/init/Android.mk index e9fd884..00d2144 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -15,31 +15,37 @@ LOCAL_SRC_FILES:= \ signal_handler.c \ init_parser.c \ ueventd.c \ - ueventd_parser.c + ueventd_parser.c \ + watchdogd.c ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c LOCAL_CFLAGS += -DBOOTCHART=1 endif +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1 +endif + LOCAL_MODULE:= init LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc - -ifeq ($(HAVE_SELINUX),true) -LOCAL_STATIC_LIBRARIES += libselinux -LOCAL_C_INCLUDES := external/libselinux/include -LOCAL_CFLAGS += -DHAVE_SELINUX -endif +LOCAL_STATIC_LIBRARIES := \ + libfs_mgr \ + libcutils \ + libc \ + libselinux include $(BUILD_EXECUTABLE) -# Make a symlink from /sbin/ueventd to /init -SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd +# Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init +SYMLINKS := \ + $(TARGET_ROOT_OUT)/sbin/ueventd \ + $(TARGET_ROOT_OUT)/sbin/watchdogd + $(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE) $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> ../$(INIT_BINARY)" diff --git a/init/builtins.c b/init/builtins.c index ad73e90..07bd6d3 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -29,14 +29,14 @@ #include <stdlib.h> #include <sys/mount.h> #include <sys/resource.h> +#include <sys/wait.h> #include <linux/loop.h> #include <cutils/partition_utils.h> #include <sys/system_properties.h> +#include <fs_mgr.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#endif #include "init.h" #include "keywords.h" @@ -75,6 +75,63 @@ static int write_file(const char *path, const char *value) } } +static int _open(const char *path) +{ + int fd; + + fd = open(path, O_RDONLY | O_NOFOLLOW); + if (fd < 0) + fd = open(path, O_WRONLY | O_NOFOLLOW); + + return fd; +} + +static int _chown(const char *path, unsigned int uid, unsigned int gid) +{ + int fd; + int ret; + + fd = _open(path); + if (fd < 0) { + return -1; + } + + ret = fchown(fd, uid, gid); + if (ret < 0) { + int errno_copy = errno; + close(fd); + errno = errno_copy; + return -1; + } + + close(fd); + + return 0; +} + +static int _chmod(const char *path, mode_t mode) +{ + int fd; + int ret; + + fd = _open(path); + if (fd < 0) { + return -1; + } + + ret = fchmod(fd, mode); + if (ret < 0) { + int errno_copy = errno; + close(fd); + errno = errno_copy; + return -1; + } + + close(fd); + + return 0; +} + static int insmod(const char *filename, char *options) { void *module; @@ -243,10 +300,10 @@ int do_mkdir(int nargs, char **args) mode = strtoul(args[2], 0, 8); } - ret = mkdir(args[1], mode); + ret = make_dir(args[1], mode); /* chmod in case the directory already exists */ if (ret == -1 && errno == EEXIST) { - ret = chmod(args[1], mode); + ret = _chmod(args[1], mode); } if (ret == -1) { return -errno; @@ -260,9 +317,17 @@ int do_mkdir(int nargs, char **args) gid = decode_uid(args[4]); } - if (chown(args[1], uid, gid)) { + if (_chown(args[1], uid, gid) < 0) { return -errno; } + + /* chown may have cleared S_ISUID and S_ISGID, chmod again */ + if (mode & (S_ISUID | S_ISGID)) { + ret = _chmod(args[1], mode); + if (ret == -1) { + return -errno; + } + } } return 0; @@ -273,12 +338,19 @@ static struct { unsigned flag; } mount_flags[] = { { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, { "nosuid", MS_NOSUID }, { "nodev", MS_NODEV }, { "nodiratime", MS_NODIRATIME }, { "ro", MS_RDONLY }, { "rw", 0 }, { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, { "defaults", 0 }, { 0, 0 }, }; @@ -375,91 +447,86 @@ int do_mount(int nargs, char **args) if (wait) wait_for_file(source, COMMAND_RETRY_TIMEOUT); if (mount(source, target, system, flags, options) < 0) { - /* If this fails, it may be an encrypted filesystem - * or it could just be wiped. If wiped, that will be - * handled later in the boot process. - * We only support encrypting /data. Check - * if we're trying to mount it, and if so, - * assume it's encrypted, mount a tmpfs instead. - * Then save the orig mount parms in properties - * for vold to query when it mounts the real - * encrypted /data. - */ - if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) { - const char *tmpfs_options; - - tmpfs_options = property_get("ro.crypto.tmpfs_options"); - - if (mount("tmpfs", target, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV, - tmpfs_options) < 0) { - return -1; - } - - /* Set the property that triggers the framework to do a minimal - * startup and ask the user for a password - */ - property_set("ro.crypto.state", "encrypted"); - property_set("vold.decrypt", "1"); - } else { - return -1; - } + return -1; } - if (!strcmp(target, DATA_MNT_POINT)) { - char fs_flags[32]; - - /* Save the original mount options */ - property_set("ro.crypto.fs_type", system); - property_set("ro.crypto.fs_real_blkdev", source); - property_set("ro.crypto.fs_mnt_point", target); - if (options) { - property_set("ro.crypto.fs_options", options); - } - snprintf(fs_flags, sizeof(fs_flags), "0x%8.8x", flags); - property_set("ro.crypto.fs_flags", fs_flags); - } } exit_success: - /* If not running encrypted, then set the property saying we are - * unencrypted, and also trigger the action for a nonencrypted system. - */ - if (!strcmp(target, DATA_MNT_POINT)) { - const char *prop; + return 0; - prop = property_get("ro.crypto.state"); - if (! prop) { - prop = "notset"; +} + +int do_mount_all(int nargs, char **args) +{ + pid_t pid; + int ret = -1; + int child_ret = -1; + int status; + const char *prop; + + if (nargs != 2) { + return -1; + } + + /* + * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and + * do the call in the child to provide protection to the main init + * process if anything goes wrong (crash or memory leak), and wait for + * the child to finish in the parent. + */ + pid = fork(); + if (pid > 0) { + /* Parent. Wait for the child to return */ + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + } else { + ret = -1; } - if (strcmp(prop, "encrypted")) { - property_set("ro.crypto.state", "unencrypted"); - action_for_each_trigger("nonencrypted", action_add_queue_tail); + } else if (pid == 0) { + /* child, call fs_mgr_mount_all() */ + klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */ + child_ret = fs_mgr_mount_all(args[1]); + if (child_ret == -1) { + ERROR("fs_mgr_mount_all returned an error\n"); } + exit(child_ret); + } else { + /* fork failed, return an error */ + return -1; } - return 0; + /* ret is 1 if the device is encrypted, 0 if not, and -1 on error */ + if (ret == 1) { + property_set("ro.crypto.state", "encrypted"); + property_set("vold.decrypt", "1"); + } else if (ret == 0) { + property_set("ro.crypto.state", "unencrypted"); + /* If fs_mgr determined this is an unencrypted device, then trigger + * that action. + */ + action_for_each_trigger("nonencrypted", action_add_queue_tail); + } + return ret; } int do_setcon(int nargs, char **args) { -#ifdef HAVE_SELINUX if (is_selinux_enabled() <= 0) return 0; if (setcon(args[1]) < 0) { return -errno; } -#endif return 0; } int do_setenforce(int nargs, char **args) { -#ifdef HAVE_SELINUX if (is_selinux_enabled() <= 0) return 0; if (security_setenforce(atoi(args[1])) < 0) { return -errno; } -#endif return 0; } @@ -643,10 +710,10 @@ out: int do_chown(int nargs, char **args) { /* GID is optional. */ if (nargs == 3) { - if (chown(args[2], decode_uid(args[1]), -1) < 0) + 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]))) + if (_chown(args[3], decode_uid(args[1]), decode_uid(args[2])) < 0) return -errno; } else { return -1; @@ -669,67 +736,47 @@ static mode_t get_mode(const char *s) { int do_chmod(int nargs, char **args) { mode_t mode = get_mode(args[1]); - if (chmod(args[2], mode) < 0) { + if (_chmod(args[2], mode) < 0) { return -errno; } return 0; } int do_restorecon(int nargs, char **args) { -#ifdef HAVE_SELINUX - char *secontext = NULL; - struct stat sb; int i; - if (is_selinux_enabled() <= 0 || !sehandle) - return 0; - for (i = 1; i < nargs; i++) { - if (lstat(args[i], &sb) < 0) - return -errno; - if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0) - return -errno; - if (lsetfilecon(args[i], secontext) < 0) { - freecon(secontext); + if (restorecon(args[i]) < 0) return -errno; - } - freecon(secontext); } -#endif return 0; } int do_setsebool(int nargs, char **args) { -#ifdef HAVE_SELINUX - SELboolean *b = alloca(nargs * sizeof(SELboolean)); - char *v; - int i; + const char *name = args[1]; + const char *value = args[2]; + SELboolean b; + int ret; if (is_selinux_enabled() <= 0) return 0; - for (i = 1; i < nargs; i++) { - char *name = args[i]; - v = strchr(name, '='); - if (!v) { - ERROR("setsebool: argument %s had no =\n", name); - return -EINVAL; - } - *v++ = 0; - b[i-1].name = name; - if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on")) - b[i-1].value = 1; - else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off")) - b[i-1].value = 0; - else { - ERROR("setsebool: invalid value %s\n", v); - return -EINVAL; - } + b.name = name; + if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on")) + b.value = 1; + else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off")) + b.value = 0; + else { + ERROR("setsebool: invalid value %s\n", value); + return -EINVAL; + } + + if (security_set_boolean_list(1, &b, 0) < 0) { + ret = -errno; + ERROR("setsebool: could not set %s to %s\n", name, value); + return ret; } - if (security_set_boolean_list(nargs - 1, b, 0) < 0) - return -errno; -#endif return 0; } @@ -753,6 +800,8 @@ int do_wait(int nargs, char **args) { if (nargs == 2) { return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT); - } - return -1; + } else if (nargs == 3) { + return wait_for_file(args[1], atoi(args[2])); + } else + return -1; } diff --git a/init/devices.c b/init/devices.c index 3b4d369..69f5fc8 100644 --- a/init/devices.c +++ b/init/devices.c @@ -30,10 +30,9 @@ #include <sys/un.h> #include <linux/netlink.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#endif +#include <selinux/android.h> #include <private/android_filesystem_config.h> #include <sys/time.h> @@ -50,10 +49,9 @@ #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" #define FIRMWARE_DIR2 "/vendor/firmware" +#define FIRMWARE_DIR3 "/firmware/image" -#ifdef HAVE_SELINUX -static struct selabel_handle *sehandle; -#endif +extern struct selabel_handle *sehandle; static int device_fd = -1; @@ -63,6 +61,7 @@ struct uevent { const char *subsystem; const char *firmware; const char *partition_name; + const char *device_name; int partition_num; int major; int minor; @@ -84,7 +83,8 @@ struct perm_node { struct platform_node { char *name; - int name_len; + char *path; + int path_len; struct listnode list; }; @@ -127,6 +127,7 @@ void fixup_sys_perms(const char *upath) char buf[512]; struct listnode *node; struct perms_ *dp; + char *secontext; /* upaths omit the "/sys" that paths in this list * contain, so we add 4 when comparing... @@ -148,6 +149,14 @@ void fixup_sys_perms(const char *upath) INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm); chown(buf, dp->uid, dp->gid); chmod(buf, dp->perm); + if (sehandle) { + secontext = NULL; + selabel_lookup(sehandle, &secontext, buf, 0); + if (secontext) { + setfilecon(buf, secontext); + freecon(secontext); + } + } } } @@ -190,17 +199,15 @@ static void make_device(const char *path, unsigned gid; mode_t mode; dev_t dev; -#ifdef HAVE_SELINUX char *secontext = NULL; -#endif mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); -#ifdef HAVE_SELINUX + if (sehandle) { selabel_lookup(sehandle, &secontext, path, mode); setfscreatecon(secontext); } -#endif + dev = makedev(major, minor); /* Temporarily change egid to avoid race condition setting the gid of the * device node. Unforunately changing the euid would prevent creation of @@ -211,95 +218,76 @@ static void make_device(const char *path, mknod(path, mode, dev); chown(path, uid, -1); setegid(AID_ROOT); -#ifdef HAVE_SELINUX - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } -#endif -} - - -static int make_dir(const char *path, mode_t mode) -{ - int rc; - -#ifdef HAVE_SELINUX - char *secontext = NULL; - - if (sehandle) { - selabel_lookup(sehandle, &secontext, path, mode); - setfscreatecon(secontext); - } -#endif - - rc = mkdir(path, mode); -#ifdef HAVE_SELINUX if (secontext) { freecon(secontext); setfscreatecon(NULL); } -#endif - return rc; } - -static void add_platform_device(const char *name) +static void add_platform_device(const char *path) { - int name_len = strlen(name); + int path_len = strlen(path); struct listnode *node; struct platform_node *bus; + const char *name = path; + + if (!strncmp(path, "/devices/", 9)) { + name += 9; + if (!strncmp(name, "platform/", 9)) + name += 9; + } list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if ((bus->name_len < name_len) && - (name[bus->name_len] == '/') && - !strncmp(name, bus->name, bus->name_len)) + if ((bus->path_len < path_len) && + (path[bus->path_len] == '/') && + !strncmp(path, bus->path, bus->path_len)) /* subdevice of an existing platform, ignore it */ return; } - INFO("adding platform device %s\n", name); + INFO("adding platform device %s (%s)\n", name, path); bus = calloc(1, sizeof(struct platform_node)); - bus->name = strdup(name); - bus->name_len = name_len; + bus->path = strdup(path); + bus->path_len = path_len; + bus->name = bus->path + (name - path); list_add_tail(&platform_names, &bus->list); } /* - * given a name that may start with a platform device, find the length of the + * given a path that may start with a platform device, find the length of the * platform device prefix. If it doesn't start with a platform device, return * 0. */ -static const char *find_platform_device(const char *name) +static struct platform_node *find_platform_device(const char *path) { - int name_len = strlen(name); + int path_len = strlen(path); struct listnode *node; struct platform_node *bus; list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if ((bus->name_len < name_len) && - (name[bus->name_len] == '/') && - !strncmp(name, bus->name, bus->name_len)) - return bus->name; + if ((bus->path_len < path_len) && + (path[bus->path_len] == '/') && + !strncmp(path, bus->path, bus->path_len)) + return bus; } return NULL; } -static void remove_platform_device(const char *name) +static void remove_platform_device(const char *path) { struct listnode *node; struct platform_node *bus; list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if (!strcmp(name, bus->name)) { - INFO("removing platform device %s\n", name); - free(bus->name); + if (!strcmp(path, bus->path)) { + INFO("removing platform device %s\n", bus->name); + free(bus->path); list_remove(node); free(bus); return; @@ -335,6 +323,7 @@ static void parse_event(const char *msg, struct uevent *uevent) uevent->minor = -1; uevent->partition_name = NULL; uevent->partition_num = -1; + uevent->device_name = NULL; /* currently ignoring SEQNUM */ while(*msg) { @@ -362,9 +351,12 @@ static void parse_event(const char *msg, struct uevent *uevent) } else if(!strncmp(msg, "PARTNAME=", 9)) { msg += 9; uevent->partition_name = msg; + } else if(!strncmp(msg, "DEVNAME=", 8)) { + msg += 8; + uevent->device_name = msg; } - /* advance to after the next \0 */ + /* advance to after the next \0 */ while(*msg++) ; } @@ -381,8 +373,10 @@ static char **get_character_device_symlinks(struct uevent *uevent) char **links; int link_num = 0; int width; + struct platform_node *pdev; - if (strncmp(uevent->path, "/devices/platform/", 18)) + pdev = find_platform_device(uevent->path); + if (!pdev) return NULL; links = malloc(sizeof(char *) * 2); @@ -391,7 +385,7 @@ static char **get_character_device_symlinks(struct uevent *uevent) memset(links, 0, sizeof(char *) * 2); /* skip "/devices/platform/<driver>" */ - parent = strchr(uevent->path + 18, '/'); + parent = strchr(uevent->path + pdev->path_len, '/'); if (!*parent) goto err; @@ -428,7 +422,7 @@ err: static char **parse_platform_block_device(struct uevent *uevent) { const char *device; - const char *path; + struct platform_node *pdev; char *slash; int width; char buf[256]; @@ -440,18 +434,16 @@ static char **parse_platform_block_device(struct uevent *uevent) unsigned int size; struct stat info; + pdev = find_platform_device(uevent->path); + if (!pdev) + return NULL; + device = pdev->name; + char **links = malloc(sizeof(char *) * 4); if (!links) return NULL; memset(links, 0, sizeof(char *) * 4); - /* Drop "/devices/platform/" */ - path = uevent->path; - device = path + 18; - device = find_platform_device(device); - if (!device) - goto err; - INFO("found platform device %s\n", device); snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); @@ -473,17 +465,13 @@ static char **parse_platform_block_device(struct uevent *uevent) links[link_num] = NULL; } - slash = strrchr(path, '/'); + slash = strrchr(uevent->path, '/'); if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0) link_num++; else links[link_num] = NULL; return links; - -err: - free(links); - return NULL; } static void handle_device(const char *action, const char *devpath, @@ -516,12 +504,12 @@ static void handle_device(const char *action, const char *devpath, static void handle_platform_device_event(struct uevent *uevent) { - const char *name = uevent->path + 18; /* length of /devices/platform/ */ + const char *path = uevent->path; if (!strcmp(uevent->action, "add")) - add_platform_device(name); + add_platform_device(path); else if (!strcmp(uevent->action, "remove")) - remove_platform_device(name); + remove_platform_device(path); } static const char *parse_device_name(struct uevent *uevent, unsigned int len) @@ -559,7 +547,7 @@ static void handle_block_device_event(struct uevent *uevent) snprintf(devpath, sizeof(devpath), "%s%s", base, name); make_dir(base, 0755); - if (!strncmp(uevent->path, "/devices/platform/", 18)) + if (!strncmp(uevent->path, "/devices/", 9)) links = parse_platform_block_device(uevent); handle_device(uevent->action, devpath, uevent->path, 1, @@ -579,18 +567,39 @@ static void handle_generic_device_event(struct uevent *uevent) if (!strncmp(uevent->subsystem, "usb", 3)) { if (!strcmp(uevent->subsystem, "usb")) { - /* This imitates the file system that would be created - * if we were using devfs instead. - * Minors are broken up into groups of 128, starting at "001" - */ - int bus_id = uevent->minor / 128 + 1; - int device_id = uevent->minor % 128 + 1; - /* build directories */ - make_dir("/dev/bus", 0755); - make_dir("/dev/bus/usb", 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); - make_dir(devpath, 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + if (uevent->device_name) { + /* + * create device node provided by kernel if present + * see drivers/base/core.c + */ + char *p = devpath; + snprintf(devpath, sizeof(devpath), "/dev/%s", uevent->device_name); + /* skip leading /dev/ */ + p += 5; + /* build directories */ + while (*p) { + if (*p == '/') { + *p = 0; + make_dir(devpath, 0755); + *p = '/'; + } + p++; + } + } + else { + /* This imitates the file system that would be created + * if we were using devfs instead. + * Minors are broken up into groups of 128, starting at "001" + */ + int bus_id = uevent->minor / 128 + 1; + int device_id = uevent->minor % 128 + 1; + /* build directories */ + make_dir("/dev/bus", 0755); + make_dir("/dev/bus/usb", 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); + make_dir(devpath, 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + } } else { /* ignore other USB events */ return; @@ -598,6 +607,9 @@ static void handle_generic_device_event(struct uevent *uevent) } else if (!strncmp(uevent->subsystem, "graphics", 8)) { base = "/dev/graphics/"; make_dir(base, 0755); + } else if (!strncmp(uevent->subsystem, "drm", 3)) { + base = "/dev/dri/"; + make_dir(base, 0755); } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { base = "/dev/oncrpc/"; make_dir(base, 0755); @@ -634,7 +646,7 @@ static void handle_generic_device_event(struct uevent *uevent) static void handle_device_event(struct uevent *uevent) { - if (!strcmp(uevent->action,"add")) + if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change")) fixup_sys_perms(uevent->path); if (!strncmp(uevent->subsystem, "block", 5)) { @@ -699,7 +711,7 @@ static int is_booting(void) static void process_firmware_event(struct uevent *uevent) { - char *root, *loading, *data, *file1 = NULL, *file2 = NULL; + char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL; int l, loading_fd, data_fd, fw_fd; int booting = is_booting(); @@ -726,6 +738,10 @@ static void process_firmware_event(struct uevent *uevent) if (l == -1) goto data_free_out; + l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware); + if (l == -1) + goto data_free_out; + loading_fd = open(loading, O_WRONLY); if(loading_fd < 0) goto file_free_out; @@ -739,17 +755,20 @@ try_loading_again: if(fw_fd < 0) { fw_fd = open(file2, O_RDONLY); if (fw_fd < 0) { - if (booting) { - /* If we're not fully booted, we may be missing - * filesystems needed for firmware, wait and retry. - */ - usleep(100000); - booting = is_booting(); - goto try_loading_again; + fw_fd = open(file3, O_RDONLY); + if (fw_fd < 0) { + if (booting) { + /* If we're not fully booted, we may be missing + * filesystems needed for firmware, wait and retry. + */ + usleep(100000); + booting = is_booting(); + goto try_loading_again; + } + INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); + write(loading_fd, "-1", 2); + goto data_close_out; } - INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); - write(loading_fd, "-1", 2); - goto data_close_out; } } @@ -870,16 +889,14 @@ void device_init(void) suseconds_t t0, t1; struct stat info; int fd; -#ifdef HAVE_SELINUX - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } - }; - if (is_selinux_enabled() > 0) - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); -#endif - /* is 64K enough? udev uses 16MB! */ - device_fd = uevent_open_socket(64*1024, true); + sehandle = NULL; + if (is_selinux_enabled() > 0) { + sehandle = selinux_android_file_context_handle(); + } + + /* is 256K enough? udev uses 16MB! */ + device_fd = uevent_open_socket(256*1024, true); if(device_fd < 0) return; diff --git a/init/init.c b/init/init.c index 26adcc6..c21a495 100755 --- a/init/init.c +++ b/init/init.c @@ -32,11 +32,9 @@ #include <sys/socket.h> #include <sys/un.h> -#ifdef HAVE_SELINUX -#include <sys/mman.h> #include <selinux/selinux.h> #include <selinux/label.h> -#endif +#include <selinux/android.h> #include <libgen.h> @@ -58,10 +56,10 @@ #include "init_parser.h" #include "util.h" #include "ueventd.h" +#include "watchdogd.h" -#ifdef HAVE_SELINUX struct selabel_handle *sehandle; -#endif +struct selabel_handle *sehandle_prop; static int property_triggers_enabled = 0; @@ -75,10 +73,7 @@ static char hardware[32]; static unsigned revision = 0; static char qemu[32]; -#ifdef HAVE_SELINUX static int selinux_enabled = 1; -static int selinux_enforcing = 0; -#endif static struct action *cur_action = NULL; static struct command *cur_command = NULL; @@ -95,7 +90,7 @@ void notify_service_state(const char *name, const char *state) } static int have_console; -static char *console_name = "/dev/console"; +static char console_name[PROP_VALUE_MAX] = "/dev/console"; static time_t process_needs_restart; static const char *ENV[32]; @@ -134,6 +129,7 @@ static void open_console() if ((fd = open(console_name, O_RDWR)) < 0) { fd = open("/dev/null", O_RDWR); } + ioctl(fd, TIOCSCTTY, 0); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); @@ -161,10 +157,9 @@ void service_start(struct service *svc, const char *dynamic_args) pid_t pid; int needs_console; int n; -#ifdef HAVE_SELINUX char *scon = NULL; int rc; -#endif + /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there @@ -201,33 +196,39 @@ void service_start(struct service *svc, const char *dynamic_args) return; } -#ifdef HAVE_SELINUX if (is_selinux_enabled() > 0) { - char *mycon = NULL, *fcon = NULL; + if (svc->seclabel) { + scon = strdup(svc->seclabel); + if (!scon) { + ERROR("Out of memory while starting '%s'\n", svc->name); + return; + } + } else { + char *mycon = NULL, *fcon = NULL; - INFO("computing context for service '%s'\n", svc->args[0]); - rc = getcon(&mycon); - if (rc < 0) { - ERROR("could not get context while starting '%s'\n", svc->name); - return; - } + INFO("computing context for service '%s'\n", svc->args[0]); + rc = getcon(&mycon); + if (rc < 0) { + ERROR("could not get context while starting '%s'\n", svc->name); + return; + } - rc = getfilecon(svc->args[0], &fcon); - if (rc < 0) { - ERROR("could not get context while starting '%s'\n", svc->name); - freecon(mycon); - return; - } + rc = getfilecon(svc->args[0], &fcon); + if (rc < 0) { + ERROR("could not get context while starting '%s'\n", svc->name); + freecon(mycon); + return; + } - rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); - freecon(mycon); - freecon(fcon); - if (rc < 0) { - ERROR("could not get context while starting '%s'\n", svc->name); - return; + rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); + freecon(mycon); + freecon(fcon); + if (rc < 0) { + ERROR("could not get context while starting '%s'\n", svc->name); + return; + } } } -#endif NOTICE("starting '%s'\n", svc->name); @@ -239,6 +240,7 @@ void service_start(struct service *svc, const char *dynamic_args) char tmp[32]; int fd, sz; + umask(077); if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); @@ -248,9 +250,7 @@ void service_start(struct service *svc, const char *dynamic_args) for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); -#ifdef HAVE_SELINUX setsockcreatecon(scon); -#endif for (si = svc->sockets; si; si = si->next) { int socket_type = ( @@ -263,11 +263,9 @@ void service_start(struct service *svc, const char *dynamic_args) } } -#ifdef HAVE_SELINUX freecon(scon); scon = NULL; setsockcreatecon(NULL); -#endif if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { @@ -313,15 +311,12 @@ void service_start(struct service *svc, const char *dynamic_args) _exit(127); } } - -#ifdef HAVE_SELINUX if (svc->seclabel) { if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) { ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno)); _exit(127); } } -#endif if (!dynamic_args) { if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { @@ -348,9 +343,7 @@ void service_start(struct service *svc, const char *dynamic_args) _exit(127); } -#ifdef HAVE_SELINUX freecon(scon); -#endif if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); @@ -447,7 +440,7 @@ static void restart_processes() static void msg_start(const char *name) { - struct service *svc; + struct service *svc = NULL; char *tmp = NULL; char *args = NULL; @@ -455,11 +448,13 @@ static void msg_start(const char *name) svc = service_find_by_name(name); else { tmp = strdup(name); - args = strchr(tmp, ':'); - *args = '\0'; - args++; + if (tmp) { + args = strchr(tmp, ':'); + *args = '\0'; + args++; - svc = service_find_by_name(tmp); + svc = service_find_by_name(tmp); + } } if (svc) { @@ -574,11 +569,9 @@ static int keychord_init_action(int nargs, char **args) static int console_init_action(int nargs, char **args) { int fd; - char tmp[PROP_VALUE_MAX]; if (console[0]) { - snprintf(tmp, sizeof(tmp), "/dev/%s", console); - console_name = strdup(tmp); + snprintf(console_name, sizeof(console_name), "/dev/%s", console); } fd = open(console_name, O_RDWR); @@ -621,13 +614,9 @@ static void import_kernel_nv(char *name, int for_emulator) *value++ = 0; if (name_len == 0) return; -#ifdef HAVE_SELINUX - if (!strcmp(name,"enforcing")) { - selinux_enforcing = atoi(value); - } else if (!strcmp(name,"selinux")) { + if (!strcmp(name,"selinux")) { selinux_enabled = atoi(value); } -#endif if (for_emulator) { /* in the emulator, export any kernel option with the @@ -656,7 +645,7 @@ static void import_kernel_nv(char *name, int for_emulator) static void export_kernel_boot_props(void) { char tmp[PROP_VALUE_MAX]; - const char *pval; + int ret; unsigned i; struct { const char *src_prop; @@ -666,27 +655,30 @@ static void export_kernel_boot_props(void) { "ro.boot.serialno", "ro.serialno", "", }, { "ro.boot.mode", "ro.bootmode", "unknown", }, { "ro.boot.baseband", "ro.baseband", "unknown", }, - { "ro.boot.carrier", "ro.carrier", "unknown", }, { "ro.boot.bootloader", "ro.bootloader", "unknown", }, }; for (i = 0; i < ARRAY_SIZE(prop_map); i++) { - pval = property_get(prop_map[i].src_prop); - property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val); + ret = property_get(prop_map[i].src_prop, tmp); + if (ret > 0) + property_set(prop_map[i].dest_prop, tmp); + else + property_set(prop_map[i].dest_prop, prop_map[i].def_val); } - pval = property_get("ro.boot.console"); - if (pval) - strlcpy(console, pval, sizeof(console)); + ret = property_get("ro.boot.console", tmp); + if (ret) + strlcpy(console, tmp, sizeof(console)); /* save a copy for init's usage during boot */ - strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode)); + property_get("ro.bootmode", tmp); + strlcpy(bootmode, tmp, sizeof(bootmode)); /* if this was given on kernel command line, override what we read * before (e.g. from /proc/cpuinfo), if anything */ - pval = property_get("ro.boot.hardware"); - if (pval) - strlcpy(hardware, pval, sizeof(hardware)); + ret = property_get("ro.boot.hardware", tmp); + if (ret) + strlcpy(hardware, tmp, sizeof(hardware)); property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); @@ -776,96 +768,63 @@ static int bootchart_init_action(int nargs, char **args) } #endif -#ifdef HAVE_SELINUX -void selinux_load_policy(void) -{ - const char path_prefix[] = "/sepolicy"; - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } - }; - char path[PATH_MAX]; - int fd, rc, vers; - struct stat sb; - void *map; +static const struct selinux_opt seopts_prop[] = { + { SELABEL_OPT_PATH, "/data/security/property_contexts" }, + { SELABEL_OPT_PATH, "/property_contexts" }, + { 0, NULL } +}; - sehandle = NULL; - if (!selinux_enabled) { - INFO("SELinux: Disabled by command line option\n"); - return; +struct selabel_handle* selinux_android_prop_context_handle(void) +{ + int i = 0; + struct selabel_handle* sehandle = NULL; + while ((sehandle == NULL) && seopts_prop[i].value) { + sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP, &seopts_prop[i], 1); + i++; } - mkdir(SELINUXMNT, 0755); - if (mount("selinuxfs", SELINUXMNT, "selinuxfs", 0, NULL)) { - if (errno == ENODEV) { - /* SELinux not enabled in kernel */ - return; - } - ERROR("SELinux: Could not mount selinuxfs: %s\n", + if (!sehandle) { + ERROR("SELinux: Could not load property_contexts: %s\n", strerror(errno)); - return; + return NULL; } - set_selinuxmnt(SELINUXMNT); + INFO("SELinux: Loaded property contexts from %s\n", seopts_prop[i - 1].value); + return sehandle; +} - vers = security_policyvers(); - if (vers <= 0) { - ERROR("SELinux: Unable to read policy version\n"); - return; - } - INFO("SELinux: Maximum supported policy version: %d\n", vers); - - snprintf(path, sizeof(path), "%s.%d", - path_prefix, vers); - fd = open(path, O_RDONLY); - while (fd < 0 && errno == ENOENT && --vers) { - snprintf(path, sizeof(path), "%s.%d", - path_prefix, vers); - fd = open(path, O_RDONLY); - } - if (fd < 0) { - ERROR("SELinux: Could not open %s: %s\n", - path, strerror(errno)); - return; - } - if (fstat(fd, &sb) < 0) { - ERROR("SELinux: Could not stat %s: %s\n", - path, strerror(errno)); - return; - } - map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (map == MAP_FAILED) { - ERROR("SELinux: Could not map %s: %s\n", - path, strerror(errno)); - return; - } +void selinux_init_all_handles(void) +{ + sehandle = selinux_android_file_context_handle(); + sehandle_prop = selinux_android_prop_context_handle(); +} - rc = security_load_policy(map, sb.st_size); - if (rc < 0) { - ERROR("SELinux: Could not load policy: %s\n", - strerror(errno)); - return; +int selinux_reload_policy(void) +{ + if (!selinux_enabled) { + return -1; } - rc = security_setenforce(selinux_enforcing); - if (rc < 0) { - ERROR("SELinux: Could not set enforcing mode to %s: %s\n", - selinux_enforcing ? "enforcing" : "permissive", strerror(errno)); - return; + INFO("SELinux: Attempting to reload policy files\n"); + + if (selinux_android_reload_policy() == -1) { + return -1; } - munmap(map, sb.st_size); - close(fd); - INFO("SELinux: Loaded policy from %s\n", path); + if (sehandle) + selabel_close(sehandle); - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); - if (!sehandle) { - ERROR("SELinux: Could not load file_contexts: %s\n", - strerror(errno)); - return; - } - INFO("SELinux: Loaded file contexts from %s\n", seopts[0].value); - return; + if (sehandle_prop) + selabel_close(sehandle_prop); + + selinux_init_all_handles(); + return 0; +} + +int audit_callback(void *data, security_class_t cls, char *buf, size_t len) +{ + snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data); + return 0; } -#endif int main(int argc, char **argv) { @@ -882,6 +841,9 @@ int main(int argc, char **argv) if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); + if (!strcmp(basename(argv[0]), "watchdogd")) + return watchdogd_main(argc, argv); + /* clear the umask */ umask(0); @@ -917,10 +879,30 @@ int main(int argc, char **argv) process_kernel_cmdline(); -#ifdef HAVE_SELINUX + union selinux_callback cb; + cb.func_log = klog_write; + selinux_set_callback(SELINUX_CB_LOG, cb); + + cb.func_audit = audit_callback; + selinux_set_callback(SELINUX_CB_AUDIT, cb); + INFO("loading selinux policy\n"); - selinux_load_policy(); -#endif + if (selinux_enabled) { + if (selinux_android_load_policy() < 0) { + selinux_enabled = 0; + INFO("SELinux: Disabled due to failed policy load\n"); + } else { + selinux_init_all_handles(); + } + } else { + INFO("SELinux: Disabled by command line option\n"); + } + /* These directories were necessarily created before initial policy load + * and therefore need their security context restored to the proper value. + * This must happen before /dev is populated by ueventd. + */ + restorecon("/dev"); + restorecon("/dev/socket"); is_charger = !strcmp(bootmode, "charger"); diff --git a/init/init.h b/init/init.h index c20864a..aa6a4ab 100644 --- a/init/init.h +++ b/init/init.h @@ -96,9 +96,7 @@ struct service { gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; -#ifdef HAVE_SELINUX char *seclabel; -#endif struct socketinfo *sockets; struct svcenvinfo *envvars; @@ -138,8 +136,8 @@ void property_changed(const char *name, const char *value); int load_565rle_image( char *file_name ); -#ifdef HAVE_SELINUX extern struct selabel_handle *sehandle; -#endif +extern struct selabel_handle *sehandle_prop; +extern int selinux_reload_policy(void); #endif /* _INIT_INIT_H */ diff --git a/init/init_parser.c b/init/init_parser.c index f538450..cce1093 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -122,6 +122,7 @@ int lookup_keyword(const char *s) break; case 'm': if (!strcmp(s, "kdir")) return K_mkdir; + if (!strcmp(s, "ount_all")) return K_mount_all; if (!strcmp(s, "ount")) return K_mount; break; case 'o': @@ -205,8 +206,9 @@ int expand_props(char *dst, const char *src, int dst_size) while (*src_ptr && left > 0) { char *c; char prop[PROP_NAME_MAX + 1]; - const char *prop_val; + char prop_val[PROP_VALUE_MAX]; int prop_len = 0; + int prop_val_len; c = strchr(src_ptr, '$'); if (!c) { @@ -264,14 +266,14 @@ int expand_props(char *dst, const char *src, int dst_size) goto err; } - prop_val = property_get(prop); - if (!prop_val) { + prop_val_len = property_get(prop, prop_val); + if (!prop_val_len) { ERROR("property '%s' doesn't exist while expanding '%s'\n", prop, src); goto err; } - ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val)); + ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len); if (ret < 0) goto err_nospace; src_ptr = c; @@ -542,7 +544,7 @@ void queue_all_property_triggers() const char* equals = strchr(name, '='); if (equals) { char prop_name[PROP_NAME_MAX + 1]; - const char* value; + char value[PROP_VALUE_MAX]; int length = equals - name; if (length > PROP_NAME_MAX) { ERROR("property name too long in trigger %s", act->name); @@ -551,9 +553,8 @@ void queue_all_property_triggers() prop_name[length] = 0; /* does the property exist, and match the trigger value? */ - value = property_get(prop_name); - if (value && (!strcmp(equals + 1, value) || - !strcmp(equals + 1, "*"))) { + property_get(prop_name, value); + if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) { action_add_queue_tail(act); } } @@ -570,6 +571,7 @@ void queue_builtin_action(int (*func)(int nargs, char **args), char *name) act = calloc(1, sizeof(*act)); act->name = name; list_init(&act->commands); + list_init(&act->qlist); cmd = calloc(1, sizeof(*cmd)); cmd->func = func; @@ -582,7 +584,9 @@ void queue_builtin_action(int (*func)(int nargs, char **args), char *name) void action_add_queue_tail(struct action *act) { - list_add_tail(&action_queue, &act->qlist); + if (list_empty(&act->qlist)) { + list_add_tail(&action_queue, &act->qlist); + } } struct action *action_remove_queue_head(void) @@ -593,6 +597,7 @@ struct action *action_remove_queue_head(void) struct listnode *node = list_head(&action_queue); struct action *act = node_to_item(node, struct action, qlist); list_remove(node); + list_init(node); return act; } } @@ -798,13 +803,11 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args } break; case K_seclabel: -#ifdef HAVE_SELINUX if (nargs != 2) { parse_error(state, "seclabel option requires a label string\n"); } else { svc->seclabel = args[1]; } -#endif break; default: @@ -826,6 +829,7 @@ static void *parse_action(struct parse_state *state, int nargs, char **args) act = calloc(1, sizeof(*act)); act->name = args[1]; list_init(&act->commands); + list_init(&act->qlist); list_add_tail(&action_list, &act->alist); /* XXX add to hash */ return act; diff --git a/init/keychords.c b/init/keychords.c index aab0819..d18a6e4 100644 --- a/init/keychords.c +++ b/init/keychords.c @@ -95,24 +95,23 @@ void keychord_init() void handle_keychord() { struct service *svc; - const char* debuggable; - const char* adb_enabled; + char debuggable[PROP_VALUE_MAX]; + char adb_enabled[PROP_VALUE_MAX]; int ret; __u16 id; // only handle keychords if ro.debuggable is set or adb is enabled. // the logic here is that bugreports should be enabled in userdebug or eng builds // and on user builds for users that are developers. - debuggable = property_get("ro.debuggable"); - adb_enabled = property_get("init.svc.adbd"); + property_get("ro.debuggable", debuggable); + property_get("init.svc.adbd", adb_enabled); ret = read(keychord_fd, &id, sizeof(id)); if (ret != sizeof(id)) { ERROR("could not read keychord id\n"); return; } - if ((debuggable && !strcmp(debuggable, "1")) || - (adb_enabled && !strcmp(adb_enabled, "running"))) { + if (!strcmp(debuggable, "1") || !strcmp(adb_enabled, "running")) { svc = service_find_by_keychord(id); if (svc) { INFO("starting service %s from keychord\n", svc->name); diff --git a/init/keywords.h b/init/keywords.h index 307c084..f188db5 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -12,6 +12,7 @@ int do_hostname(int nargs, char **args); int do_ifup(int nargs, char **args); int do_insmod(int nargs, char **args); int do_mkdir(int nargs, char **args); +int do_mount_all(int nargs, char **args); int do_mount(int nargs, char **args); int do_restart(int nargs, char **args); int do_restorecon(int nargs, char **args); @@ -60,6 +61,7 @@ enum { KEYWORD(import, SECTION, 1, 0) KEYWORD(keycodes, OPTION, 0, 0) KEYWORD(mkdir, COMMAND, 1, do_mkdir) + KEYWORD(mount_all, COMMAND, 1, do_mount_all) KEYWORD(mount, COMMAND, 3, do_mount) KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) @@ -76,7 +78,7 @@ enum { KEYWORD(setkey, COMMAND, 0, do_setkey) KEYWORD(setprop, COMMAND, 2, do_setprop) KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) - KEYWORD(setsebool, COMMAND, 1, do_setsebool) + KEYWORD(setsebool, COMMAND, 2, do_setsebool) KEYWORD(socket, OPTION, 0, 0) KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) diff --git a/init/property_service.c b/init/property_service.c index 059db97..86e35f1 100644..100755 --- a/init/property_service.c +++ b/init/property_service.c @@ -40,6 +40,9 @@ #include <sys/atomics.h> #include <private/android_filesystem_config.h> +#include <selinux/selinux.h> +#include <selinux/label.h> + #include "property_service.h" #include "init.h" #include "util.h" @@ -76,8 +79,10 @@ struct { { "sys.", AID_SYSTEM, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, + { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, + { "debug.", AID_SYSTEM, 0 }, { "debug.", AID_SHELL, 0 }, { "log.", AID_SHELL, 0 }, { "service.adb.root", AID_SHELL, 0 }, @@ -85,6 +90,8 @@ struct { { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, + { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, + { "selinux." , AID_SYSTEM, 0 }, { NULL, 0, 0 } }; @@ -116,7 +123,7 @@ static int init_workspace(workspace *w, size_t size) /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let's do that... */ - fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600); + fd = open("/dev/__properties__", O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (fd < 0) return -1; @@ -129,7 +136,7 @@ static int init_workspace(workspace *w, size_t size) close(fd); - fd = open("/dev/__properties__", O_RDONLY); + fd = open("/dev/__properties__", O_RDONLY | O_NOFOLLOW); if (fd < 0) return -1; @@ -145,23 +152,11 @@ out: return -1; } -/* (8 header words + 247 toc words) = 1020 bytes */ -/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */ - -#define PA_COUNT_MAX 247 -#define PA_INFO_START 1024 -#define PA_SIZE 32768 - static workspace pa_workspace; -static prop_info *pa_info_array; - -extern prop_area *__system_property_area__; static int init_property_area(void) { - prop_area *pa; - - if(pa_info_array) + if (property_area_inited) return -1; if(init_workspace(&pa_workspace, PA_SIZE)) @@ -169,25 +164,54 @@ static int init_property_area(void) fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); - pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START); + __system_property_area_init(pa_workspace.data); - pa = pa_workspace.data; - memset(pa, 0, PA_SIZE); - pa->magic = PROP_AREA_MAGIC; - pa->version = PROP_AREA_VERSION; - - /* plug into the lib property services */ - __system_property_area__ = pa; property_area_inited = 1; return 0; } -static void update_prop_info(prop_info *pi, const char *value, unsigned len) +static int check_mac_perms(const char *name, char *sctx) { - pi->serial = pi->serial | 1; - memcpy(pi->value, value, len + 1); - pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); - __futex_wake(&pi->serial, INT32_MAX); + if (is_selinux_enabled() <= 0) + return 1; + + char *tctx = NULL; + const char *class = "property_service"; + const char *perm = "set"; + int result = 0; + + if (!sctx) + goto err; + + if (!sehandle_prop) + goto err; + + if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) + goto err; + + if (selinux_check_access(sctx, tctx, class, perm, name) == 0) + result = 1; + + freecon(tctx); + err: + return result; +} + +static int check_control_mac_perms(const char *name, char *sctx) +{ + /* + * Create a name prefix out of ctl.<service name> + * The new prefix allows the use of the existing + * property service backend labeling while avoiding + * mislabels based on true property prefixes. + */ + char ctl_name[PROP_VALUE_MAX+4]; + int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name); + + if (ret < 0 || (size_t) ret >= sizeof(ctl_name)) + return 0; + + return check_mac_perms(ctl_name, sctx); } /* @@ -196,17 +220,18 @@ static void update_prop_info(prop_info *pi, const char *value, unsigned len) * * Returns 1 if uid allowed, 0 otherwise. */ -static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) { +static int check_control_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) { + int i; if (uid == AID_SYSTEM || uid == AID_ROOT) - return 1; + return check_control_mac_perms(name, sctx); /* Search the ACL */ for (i = 0; control_perms[i].service; i++) { if (strcmp(control_perms[i].service, name) == 0) { if ((uid && control_perms[i].uid == uid) || (gid && control_perms[i].gid == gid)) { - return 1; + return check_control_mac_perms(name, sctx); } } } @@ -217,22 +242,22 @@ static int check_control_perms(const char *name, unsigned int uid, unsigned int * Checks permissions for setting system properties. * Returns 1 if uid allowed, 0 otherwise. */ -static int check_perms(const char *name, unsigned int uid, unsigned int gid) +static int check_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) { int i; - if (uid == 0) - return 1; - if(!strncmp(name, "ro.", 3)) name +=3; + if (uid == 0) + return check_mac_perms(name, sctx); + for (i = 0; property_perms[i].prefix; i++) { - int tmp; if (strncmp(property_perms[i].prefix, name, strlen(property_perms[i].prefix)) == 0) { if ((uid && property_perms[i].uid == uid) || (gid && property_perms[i].gid == gid)) { - return 1; + + return check_mac_perms(name, sctx); } } } @@ -240,30 +265,19 @@ static int check_perms(const char *name, unsigned int uid, unsigned int gid) return 0; } -const char* property_get(const char *name) +int __property_get(const char *name, char *value) { - prop_info *pi; - - if(strlen(name) >= PROP_NAME_MAX) return 0; - - pi = (prop_info*) __system_property_find(name); - - if(pi != 0) { - return pi->value; - } else { - return 0; - } + return __system_property_get(name, value); } static void write_persistent_property(const char *name, const char *value) { - const char *tempPath = PERSISTENT_PROPERTY_DIR "/.temp"; + char tempPath[PATH_MAX]; char path[PATH_MAX]; - int fd, length; - - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); + int fd; - fd = open(tempPath, O_WRONLY|O_CREAT|O_TRUNC, 0600); + snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR); + fd = mkstemp(tempPath); if (fd < 0) { ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno); return; @@ -271,6 +285,7 @@ static void write_persistent_property(const char *name, const char *value) write(fd, value, strlen(value)); close(fd); + snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); if (rename(tempPath, path)) { unlink(tempPath); ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path); @@ -279,11 +294,11 @@ static void write_persistent_property(const char *name, const char *value) int property_set(const char *name, const char *value) { - prop_area *pa; prop_info *pi; + int ret; - int namelen = strlen(name); - int valuelen = strlen(value); + size_t namelen = strlen(name); + size_t valuelen = strlen(value); if(namelen >= PROP_NAME_MAX) return -1; if(valuelen >= PROP_VALUE_MAX) return -1; @@ -295,25 +310,13 @@ int property_set(const char *name, const char *value) /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; - pa = __system_property_area__; - update_prop_info(pi, value, valuelen); - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); + __system_property_update(pi, value, valuelen); } else { - pa = __system_property_area__; - if(pa->count == PA_COUNT_MAX) return -1; - - pi = pa_info_array + pa->count; - pi->serial = (valuelen << 24); - memcpy(pi->name, name, namelen + 1); - memcpy(pi->value, value, valuelen + 1); - - pa->toc[pa->count] = - (namelen << 24) | (((unsigned) pi) - ((unsigned) pa)); - - pa->count++; - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); + ret = __system_property_add(name, namelen, value, valuelen); + if (ret < 0) { + ERROR("Failed to set '%s'='%s'", name, value); + return ret; + } } /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { @@ -333,26 +336,14 @@ int property_set(const char *name, const char *value) * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); + } else if (strcmp("selinux.reload_policy", name) == 0 && + strcmp("1", value) == 0) { + selinux_reload_policy(); } property_changed(name, value); return 0; } -static int property_list(void (*propfn)(const char *key, const char *value, void *cookie), - void *cookie) -{ - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; - const prop_info *pi; - unsigned n; - - for(n = 0; (pi = __system_property_find_nth(n)); n++) { - __system_property_read(pi, name, value); - propfn(name, value, cookie); - } - return 0; -} - void handle_property_set_fd() { prop_msg msg; @@ -363,6 +354,7 @@ void handle_property_set_fd() struct sockaddr_un addr; socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); + char * source_ctx = NULL; if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; @@ -371,13 +363,13 @@ void handle_property_set_fd() /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); - ERROR("Unable to recieve socket options\n"); + ERROR("Unable to receive socket options\n"); return; } r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { - ERROR("sys_prop: mis-match msg size recieved: %d expected: %d errno: %d\n", + ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; @@ -388,18 +380,20 @@ void handle_property_set_fd() msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; + getpeercon(s, &source_ctx); + if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling // ctl.* properties. close(s); - if (check_control_perms(msg.value, cr.uid, cr.gid)) { + if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) { handle_control_message((char*) msg.name + 4, (char*) msg.value); } else { ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n", msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); } } else { - if (check_perms(msg.name, cr.uid, cr.gid)) { + if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) { property_set((char*) msg.name, (char*) msg.value); } else { ERROR("sys_prop: permission denied uid:%d name:%s\n", @@ -411,6 +405,7 @@ void handle_property_set_fd() // the property is written to memory. close(s); } + freecon(source_ctx); break; default: @@ -468,12 +463,14 @@ static void load_properties_from_file(const char *fn) static void load_persistent_properties() { DIR* dir = opendir(PERSISTENT_PROPERTY_DIR); + int dir_fd; struct dirent* entry; - char path[PATH_MAX]; char value[PROP_VALUE_MAX]; int fd, length; + struct stat sb; if (dir) { + dir_fd = dirfd(dir); while ((entry = readdir(dir)) != NULL) { if (strncmp("persist.", entry->d_name, strlen("persist."))) continue; @@ -482,20 +479,39 @@ static void load_persistent_properties() continue; #endif /* open the file and read the property value */ - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, entry->d_name); - fd = open(path, O_RDONLY); - if (fd >= 0) { - length = read(fd, value, sizeof(value) - 1); - if (length >= 0) { - value[length] = 0; - property_set(entry->d_name, value); - } else { - ERROR("Unable to read persistent property file %s errno: %d\n", path, errno); - } + fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW); + if (fd < 0) { + ERROR("Unable to open persistent property file \"%s\" errno: %d\n", + entry->d_name, errno); + continue; + } + if (fstat(fd, &sb) < 0) { + ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno); + close(fd); + continue; + } + + // File must not be accessible to others, be owned by root/root, and + // not be a hard link to any other file. + if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) + || (sb.st_uid != 0) + || (sb.st_gid != 0) + || (sb.st_nlink != 1)) { + ERROR("skipping insecure property file %s (uid=%lu gid=%lu nlink=%d mode=%o)\n", + entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); close(fd); + continue; + } + + length = read(fd, value, sizeof(value) - 1); + if (length >= 0) { + value[length] = 0; + property_set(entry->d_name, value); } else { - ERROR("Unable to open persistent property file %s errno: %d\n", path, errno); + ERROR("Unable to read persistent property file %s errno: %d\n", + entry->d_name, errno); } + close(fd); } closedir(dir); } else { @@ -520,6 +536,19 @@ int properties_inited(void) return property_area_inited; } +static void load_override_properties() { +#ifdef ALLOW_LOCAL_PROP_OVERRIDE + char debuggable[PROP_VALUE_MAX]; + int ret; + + ret = property_get("ro.debuggable", debuggable); + if (ret && (strcmp(debuggable, "1") == 0)) { + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + } +#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ +} + + /* When booting an encrypted system, /data is not mounted when the * property service is started, so any properties stored there are * not loaded. Vold triggers init to load these properties once it @@ -527,7 +556,7 @@ int properties_inited(void) */ void load_persist_props(void) { - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); } @@ -538,7 +567,7 @@ void start_property_service(void) load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); diff --git a/init/property_service.h b/init/property_service.h index b9d1bf6..46cbd8f 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -18,6 +18,7 @@ #define _INIT_PROPERTY_H #include <stdbool.h> +#include <sys/system_properties.h> extern void handle_property_set_fd(void); extern void property_init(void); @@ -25,9 +26,25 @@ extern void property_load_boot_defaults(void); extern void load_persist_props(void); extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); -extern const char* property_get(const char *name); +extern int __property_get(const char *name, char *value); extern int property_set(const char *name, const char *value); extern int properties_inited(); int get_property_set_fd(void); +extern void __property_get_size_error() + __attribute__((__error__("property_get called with too small buffer"))); + +static inline +__attribute__ ((always_inline)) +__attribute__ ((gnu_inline)) +__attribute__ ((artificial)) +int property_get(const char *name, char *value) +{ + size_t value_len = __builtin_object_size(value, 0); + if (value_len != PROP_VALUE_MAX) + __property_get_size_error(); + + return __property_get(name, value); +} + #endif /* _INIT_PROPERTY_H */ diff --git a/init/readme.txt b/init/readme.txt index df524a6..7a5997d 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -88,6 +88,13 @@ group <groupname> [ <groupname> ]* supplemental groups of the process (via setgroups()). Currently defaults to root. (??? probably should default to nobody) +seclabel <securitycontext> + Change to securitycontext before exec'ing this service. + Primarily for use by services run from the rootfs, e.g. ueventd, adbd. + Services on the system partition can instead use policy-defined transitions + based on their file security context. + If not specified and no transition is defined in policy, defaults to the init context. + oneshot Do not restart the service when it exits. @@ -182,6 +189,21 @@ mount <type> <device> <dir> [ <mountoption> ]* device by name. <mountoption>s include "ro", "rw", "remount", "noatime", ... +restorecon <path> + Restore the file named by <path> to the security context specified + in the file_contexts configuration. + Not required for directories created by the init.rc as these are + automatically labeled correctly by init. + +setcon <securitycontext> + Set the current process security context to the specified string. + This is typically only used from early-init to set the init context + before any other process is started. + +setenforce 0|1 + Set the SELinux system-wide enforcing status. + 0 is permissive (i.e. log but do not deny), 1 is enforcing. + setkey TBD @@ -191,6 +213,10 @@ setprop <name> <value> setrlimit <resource> <cur> <max> Set the rlimit for a resource. +setsebool <name> <value> + Set SELinux boolean <name> to <value>. + <value> may be 1|true|on or 0|false|off + start <service> Start a service running if it is not already running. @@ -207,6 +233,11 @@ trigger <event> Trigger an event. Used to queue an action from another action. +wait <path> [ <timeout> ] + Poll for the existence of the given file and return when found, + or the timeout has been reached. If timeout is not specified it + currently defaults to five seconds. + write <path> <string> [ <string> ]* Open the file at <path> and write one or more strings to it with write(2) diff --git a/init/signal_handler.c b/init/signal_handler.c index 672904d..d31ad63 100644 --- a/init/signal_handler.c +++ b/init/signal_handler.c @@ -133,11 +133,9 @@ void signal_init(void) int s[2]; struct sigaction act; - + memset(&act, 0, sizeof(act)); act.sa_handler = sigchld_handler; act.sa_flags = SA_NOCLDSTOP; - act.sa_mask = 0; - act.sa_restorer = NULL; sigaction(SIGCHLD, &act, 0); /* create a signalling mechanism for the sigchld handler */ diff --git a/init/ueventd.c b/init/ueventd.c index ecf3b9b..a41c31e 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -53,11 +53,18 @@ int ueventd_main(int argc, char **argv) int nr; char tmp[32]; - /* Prevent fire-and-forget children from becoming zombies. - * If we should need to wait() for some children in the future - * (as opposed to none right now), double-forking here instead - * of ignoring SIGCHLD may be the better solution. - */ + /* + * init sets the umask to 077 for forked processes. We need to + * create files with exact permissions, without modification by + * the umask. + */ + umask(000); + + /* Prevent fire-and-forget children from becoming zombies. + * If we should need to wait() for some children in the future + * (as opposed to none right now), double-forking here instead + * of ignoring SIGCHLD may be the better solution. + */ signal(SIGCHLD, SIG_IGN); open_devnull_stdio(); @@ -98,7 +105,7 @@ static int get_android_id(const char *id) for (i = 0; i < ARRAY_SIZE(android_ids); i++) if (!strcmp(id, android_ids[i].name)) return android_ids[i].aid; - return 0; + return -1; } void set_device_permission(int nargs, char **args) diff --git a/init/util.c b/init/util.c index 3a4b10b..918bc05 100755 --- a/init/util.c +++ b/init/util.c @@ -23,9 +23,7 @@ #include <errno.h> #include <time.h> -#ifdef HAVE_SELINUX #include <selinux/label.h> -#endif #include <sys/stat.h> #include <sys/types.h> @@ -47,7 +45,7 @@ */ static unsigned int android_name_to_id(const char *name) { - struct android_id_info *info = android_ids; + const struct android_id_info *info = android_ids; unsigned int n; for (n = 0; n < android_id_count; n++) { @@ -89,9 +87,7 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) { struct sockaddr_un addr; int fd, ret; -#ifdef HAVE_SELINUX char *secon; -#endif fd = socket(PF_UNIX, type, 0); if (fd < 0) { @@ -110,14 +106,12 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_close; } -#ifdef HAVE_SELINUX secon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(secon); } -#endif ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); if (ret) { @@ -125,10 +119,8 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_unlink; } -#ifdef HAVE_SELINUX setfscreatecon(NULL); freecon(secon); -#endif chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); @@ -151,11 +143,23 @@ void *read_file(const char *fn, unsigned *_sz) char *data; int sz; int fd; + struct stat sb; data = 0; fd = open(fn, O_RDONLY); if(fd < 0) return 0; + // for security reasons, disallow world-writable + // or group-writable files + if (fstat(fd, &sb) < 0) { + ERROR("fstat failed for '%s'\n", fn); + goto oops; + } + if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) { + ERROR("skipping insecure file '%s'\n", fn); + goto oops; + } + sz = lseek(fd, 0, SEEK_END); if(sz < 0) goto oops; @@ -290,12 +294,12 @@ int mkdir_recursive(const char *pathname, mode_t mode) memcpy(buf, pathname, width); buf[width] = 0; if (stat(buf, &info) != 0) { - ret = mkdir(buf, mode); + ret = make_dir(buf, mode); if (ret && errno != EEXIST) return ret; } } - ret = mkdir(pathname, mode); + ret = make_dir(pathname, mode); if (ret && errno != EEXIST) return ret; return 0; @@ -451,3 +455,47 @@ void import_kernel_cmdline(int in_qemu, ptr = x; } } + +int make_dir(const char *path, mode_t mode) +{ + int rc; + + char *secontext = NULL; + + if (sehandle) { + selabel_lookup(sehandle, &secontext, path, mode); + setfscreatecon(secontext); + } + + rc = mkdir(path, mode); + + if (secontext) { + int save_errno = errno; + freecon(secontext); + setfscreatecon(NULL); + errno = save_errno; + } + + return rc; +} + +int restorecon(const char *pathname) +{ + char *secontext = NULL; + struct stat sb; + int i; + + if (is_selinux_enabled() <= 0 || !sehandle) + return 0; + + if (lstat(pathname, &sb) < 0) + return -errno; + if (selabel_lookup(sehandle, &secontext, pathname, sb.st_mode) < 0) + return -errno; + if (lsetfilecon(pathname, secontext) < 0) { + freecon(secontext); + return -errno; + } + freecon(secontext); + return 0; +} diff --git a/init/util.h b/init/util.h index 9247739..45905b6 100644 --- a/init/util.h +++ b/init/util.h @@ -39,4 +39,6 @@ int wait_for_file(const char *filename, int timeout); void open_devnull_stdio(void); void get_hardware_name(char *hardware, unsigned int *revision); void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu)); +int make_dir(const char *path, mode_t mode); +int restorecon(const char *pathname); #endif diff --git a/init/watchdogd.c b/init/watchdogd.c new file mode 100644 index 0000000..fb53836 --- /dev/null +++ b/init/watchdogd.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 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 <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include <linux/watchdog.h> + +#include "log.h" +#include "util.h" + +#define DEV_NAME "/dev/watchdog" + +int watchdogd_main(int argc, char **argv) +{ + int fd; + int ret; + int interval = 10; + int margin = 10; + int timeout; + + open_devnull_stdio(); + klog_init(); + + INFO("Starting watchdogd\n"); + + if (argc >= 2) + interval = atoi(argv[1]); + + if (argc >= 3) + margin = atoi(argv[2]); + + timeout = interval + margin; + + fd = open(DEV_NAME, O_RDWR); + if (fd < 0) { + ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno)); + return 1; + } + + ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno)); + ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno)); + } else { + if (timeout > margin) + interval = timeout - margin; + else + interval = 1; + ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n", + timeout, interval, margin); + } + } + + while(1) { + write(fd, "", 1); + sleep(interval); + } +} + diff --git a/init/watchdogd.h b/init/watchdogd.h new file mode 100644 index 0000000..8b48ab8 --- /dev/null +++ b/init/watchdogd.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef _INIT_WATCHDOGD_H_ +#define _INIT_WATCHDOGD_H_ + +int watchdogd_main(int argc, char **argv); + +#endif |
