diff options
47 files changed, 3087 insertions, 2006 deletions
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index db8b018..993206f 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -71,6 +71,10 @@ #define VENDOR_ID_QUALCOMM 0x05c6 // On-The-Go-Video's USB Vendor ID #define VENDOR_ID_OTGV 0x2257 +// NEC's USB Vendor ID +#define VENDOR_ID_NEC 0x0409 +// Panasonic Mobile Communication's USB Vendor ID +#define VENDOR_ID_PMC 0x04DA /** built-in vendor list */ @@ -93,6 +97,8 @@ int builtInVendorIds[] = { VENDOR_ID_PANTECH, VENDOR_ID_QUALCOMM, VENDOR_ID_OTGV, + VENDOR_ID_NEC, + VENDOR_ID_PMC, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index bed30b2..f3bfbeba 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -150,6 +150,8 @@ int match_fastboot(usb_ifc_info *info) (info->dev_vendor != 0x18d1) && // Google (info->dev_vendor != 0x0451) && (info->dev_vendor != 0x0502) && + (info->dev_vendor != 0x0fce) && // Sony Ericsson + (info->dev_vendor != 0x05c6) && // Qualcomm (info->dev_vendor != 0x22b8) && // Motorola (info->dev_vendor != 0x0955) && // Nvidia (info->dev_vendor != 0x413c) && // DELL diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index 0b0512d..9488687 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -64,7 +64,7 @@ struct usb_handle /** Try out all the interfaces and see if there's a match. Returns 0 on * success, -1 on failure. */ -static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) { +static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) { IOReturn kr; IOUSBFindInterfaceRequest request; io_iterator_t iterator; @@ -515,8 +515,29 @@ int usb_write(usb_handle *h, const void *data, int len) { return -1; } +#if 0 result = (*h->interface)->WritePipe( h->interface, h->bulkOut, (void *)data, len); +#else + /* Attempt to work around crashes in the USB driver that may be caused + * by trying to write too much data at once. The kernel IOCopyMapper + * panics if a single iovmAlloc needs more than half of its mapper pages. + */ + const int maxLenToSend = 1048576; // 1 MiB + int lenRemaining = len; + result = 0; + while (lenRemaining > 0) { + int lenToSend = lenRemaining > maxLenToSend + ? maxLenToSend : lenRemaining; + + result = (*h->interface)->WritePipe( + h->interface, h->bulkOut, (void *)data, lenToSend); + if (result != 0) break; + + lenRemaining -= lenToSend; + data = (const char*)data + lenToSend; + } +#endif #if 0 if ((result == 0) && (h->zero_mask)) { diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h new file mode 100644 index 0000000..1c23be9 --- /dev/null +++ b/include/cutils/atomic-inline.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 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 ANDROID_CUTILS_ATOMIC_INLINE_H +#define ANDROID_CUTILS_ATOMIC_INLINE_H + +/* + * Inline declarations and macros for some special-purpose atomic + * operations. These are intended for rare circumstances where a + * memory barrier needs to be issued inline rather than as a function + * call. + * + * Most code should not use these. + * + * Anything that does include this file must set ANDROID_SMP to either + * 0 or 1, indicating compilation for UP or SMP, respectively. + * + * Macros defined in this header: + * + * void ANDROID_MEMBAR_FULL(void) + * Full memory barrier. Provides a compiler reordering barrier, and + * on SMP systems emits an appropriate instruction. + */ + +#if !defined(ANDROID_SMP) +# error "Must define ANDROID_SMP before including atomic-inline.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Define the full memory barrier for an SMP system. This is + * platform-specific. + */ + +#ifdef __arm__ +#include <machine/cpu-features.h> + +/* + * For ARMv6K we need to issue a specific MCR instead of the DMB, since + * that wasn't added until v7. For anything older, SMP isn't relevant. + * Since we don't have an ARMv6K to test with, we're not going to deal + * with that now. + * + * The DMB instruction is found in the ARM and Thumb2 instruction sets. + * This will fail on plain 16-bit Thumb. + */ +#if defined(__ARM_HAVE_DMB) +# define _ANDROID_MEMBAR_FULL_SMP() \ + do { __asm__ __volatile__ ("dmb" ::: "memory"); } while (0) +#else +# define _ANDROID_MEMBAR_FULL_SMP() ARM_SMP_defined_but_no_DMB() +#endif + +#elif defined(__i386__) || defined(__x86_64__) +/* + * For recent x86, we can use the SSE2 mfence instruction. + */ +# define _ANDROID_MEMBAR_FULL_SMP() \ + do { __asm__ __volatile__ ("mfence" ::: "memory"); } while (0) + +#else +/* + * Implementation not defined for this platform. Hopefully we're building + * in uniprocessor mode. + */ +# define _ANDROID_MEMBAR_FULL_SMP() SMP_barrier_not_defined_for_platform() +#endif + + +/* + * Full barrier. On uniprocessors this is just a compiler reorder barrier, + * which ensures that the statements appearing above the barrier in the C/C++ + * code will be issued after the statements appearing below the barrier. + * + * For SMP this also includes a memory barrier instruction. On an ARM + * CPU this means that the current core will flush pending writes, wait + * for pending reads to complete, and discard any cached reads that could + * be stale. Other CPUs may do less, but the end result is equivalent. + */ +#if ANDROID_SMP != 0 +# define ANDROID_MEMBAR_FULL() _ANDROID_MEMBAR_FULL_SMP() +#else +# define ANDROID_MEMBAR_FULL() \ + do { __asm__ __volatile__ ("" ::: "memory"); } while (0) +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_CUTILS_ATOMIC_INLINE_H diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h index 5694d66..0200709 100644 --- a/include/cutils/atomic.h +++ b/include/cutils/atomic.h @@ -25,53 +25,103 @@ extern "C" { #endif /* - * NOTE: memory shared between threads is synchronized by all atomic operations - * below, this means that no explicit memory barrier is required: all reads or - * writes issued before android_atomic_* operations are guaranteed to complete - * before the atomic operation takes place. + * A handful of basic atomic operations. The appropriate pthread + * functions should be used instead of these whenever possible. + * + * The "acquire" and "release" terms can be defined intuitively in terms + * of the placement of memory barriers in a simple lock implementation: + * - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds + * - barrier + * - [do work] + * - barrier + * - store(lock-is-free) + * In very crude terms, the initial (acquire) barrier prevents any of the + * "work" from happening before the lock is held, and the later (release) + * barrier ensures that all of the work happens before the lock is released. + * (Think of cached writes, cache read-ahead, and instruction reordering + * around the CAS and store instructions.) + * + * The barriers must apply to both the compiler and the CPU. Note it is + * legal for instructions that occur before an "acquire" barrier to be + * moved down below it, and for instructions that occur after a "release" + * barrier to be moved up above it. + * + * The ARM-driven implementation we use here is short on subtlety, + * and actually requests a full barrier from the compiler and the CPU. + * The only difference between acquire and release is in whether they + * are issued before or after the atomic operation with which they + * are associated. To ease the transition to C/C++ atomic intrinsics, + * you should not rely on this, and instead assume that only the minimal + * acquire/release protection is provided. + * + * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries. + * If they are not, atomicity is not guaranteed. */ -void android_atomic_write(int32_t value, volatile int32_t* addr); - /* - * all these atomic operations return the previous value + * Basic arithmetic and bitwise operations. These all provide a + * barrier with "release" ordering, and return the previous value. + * + * These have the same characteristics (e.g. what happens on overflow) + * as the equivalent non-atomic C operations. */ - - int32_t android_atomic_inc(volatile int32_t* addr); int32_t android_atomic_dec(volatile int32_t* addr); - int32_t android_atomic_add(int32_t value, volatile int32_t* addr); int32_t android_atomic_and(int32_t value, volatile int32_t* addr); int32_t android_atomic_or(int32_t value, volatile int32_t* addr); -int32_t android_atomic_swap(int32_t value, volatile int32_t* addr); +/* + * Perform an atomic load with "acquire" or "release" ordering. + * + * This is only necessary if you need the memory barrier. A 32-bit read + * from a 32-bit aligned address is atomic on all supported platforms. + */ +int32_t android_atomic_acquire_load(volatile int32_t* addr); +int32_t android_atomic_release_load(volatile int32_t* addr); /* - * NOTE: Two "quasiatomic" operations on the exact same memory address - * are guaranteed to operate atomically with respect to each other, - * but no guarantees are made about quasiatomic operations mixed with - * non-quasiatomic operations on the same address, nor about - * quasiatomic operations that are performed on partially-overlapping - * memory. + * Perform an atomic store with "acquire" or "release" ordering. + * + * This is only necessary if you need the memory barrier. A 32-bit write + * to a 32-bit aligned address is atomic on all supported platforms. */ +void android_atomic_acquire_store(int32_t value, volatile int32_t* addr); +void android_atomic_release_store(int32_t value, volatile int32_t* addr); -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr); -int64_t android_quasiatomic_read_64(volatile int64_t* addr); - /* - * cmpxchg return a non zero value if the exchange was NOT performed, - * in other words if oldvalue != *addr + * Unconditional swap operation with "acquire" or "release" ordering. + * + * Stores the new value at *addr, and returns the previous value. */ +int32_t android_atomic_acquire_swap(int32_t value, volatile int32_t* addr); +int32_t android_atomic_release_swap(int32_t value, volatile int32_t* addr); -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, +/* + * Compare-and-set operation with "acquire" or "release" ordering. + * + * This returns zero if the new value was successfully stored, which will + * only happen when *addr == oldvalue. + * + * (The return value is inverted from implementations on other platforms, + * but matches the ARM ldrex/strex result.) + * + * Implementations that use the release CAS in a loop may be less efficient + * than possible, because we re-issue the memory barrier on each iteration. + */ +int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr); +int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr); -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr); - +/* + * Aliases for code using an older version of this header. These are now + * deprecated and should not be used. The definitions will be removed + * in a future release. + */ +#define android_atomic_write android_atomic_release_store +#define android_atomic_cmpxchg android_atomic_release_cas - #ifdef __cplusplus } // extern "C" #endif diff --git a/init/Android.mk b/init/Android.mk index d3766d4..162c226 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -10,7 +10,12 @@ LOCAL_SRC_FILES:= \ property_service.c \ util.c \ parser.c \ - logo.c + logo.c \ + keychords.c \ + signal_handler.c \ + init_parser.c \ + ueventd.c \ + ueventd_parser.c ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c @@ -25,9 +30,20 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) LOCAL_STATIC_LIBRARIES := libcutils libc -#LOCAL_STATIC_LIBRARIES := libcutils libc libminui libpixelflinger_static -#LOCAL_STATIC_LIBRARIES += libminzip libunz libamend libmtdutils libmincrypt -#LOCAL_STATIC_LIBRARIES += libstdc++_static - include $(BUILD_EXECUTABLE) +# Make a symlink from /sbin/ueventd to /init +SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd +$(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE) +$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk + @echo "Symlink: $@ -> ../$(INIT_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf ../$(INIT_BINARY) $@ + +ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS) + +# We need this so that the installed files could be picked up based on the +# local module name +ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \ + $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(SYMLINKS) diff --git a/init/builtins.c b/init/builtins.c index 44faf17..e0ccf9f 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -35,6 +35,9 @@ #include "keywords.h" #include "property_service.h" #include "devices.h" +#include "init_parser.h" +#include "util.h" +#include "log.h" #include <private/android_filesystem_config.h> @@ -218,7 +221,7 @@ int do_insmod(int nargs, char **args) int do_import(int nargs, char **args) { - return parse_config_file(args[1]); + return init_parse_config_file(args[1]); } int do_mkdir(int nargs, char **args) @@ -275,6 +278,7 @@ int do_mount(int nargs, char **args) char *options = NULL; unsigned flags = 0; int n, i; + int wait = 0; for (n = 4; n < nargs; n++) { for (i = 0; mount_flags[i].name; i++) { @@ -284,9 +288,13 @@ int do_mount(int nargs, char **args) } } - /* 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]; + if (!mount_flags[i].name) { + if (!strcmp(args[n], "wait")) + wait = 1; + /* if our last argument isn't a flag, wolf it up as an option string */ + else if (n + 1 == nargs) + options = args[n]; + } } system = args[1]; @@ -301,6 +309,8 @@ int do_mount(int nargs, char **args) sprintf(tmp, "/dev/block/mtdblock%d", n); + if (wait) + wait_for_file(tmp, COMMAND_RETRY_TIMEOUT); if (mount(tmp, target, system, flags, options) < 0) { return -1; } @@ -347,6 +357,8 @@ int do_mount(int nargs, char **args) ERROR("out of loopback devices"); return -1; } else { + if (wait) + wait_for_file(source, COMMAND_RETRY_TIMEOUT); if (mount(source, target, system, flags, options) < 0) { return -1; } @@ -414,7 +426,6 @@ int do_restart(int nargs, char **args) int do_trigger(int nargs, char **args) { action_for_each_trigger(args[1], action_add_queue_tail); - drain_action_queue(); return 0; } @@ -547,29 +558,10 @@ int do_loglevel(int nargs, char **args) { 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; - } +int do_wait(int nargs, char **args) +{ + if (nargs == 2) { + return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT); } - add_devperms_partners(source, get_mode(args[2]), decode_uid(args[3]), - decode_uid(args[4]), prefix); - return 0; + return -1; } diff --git a/init/devices.c b/init/devices.c index 8789b89..155a41e 100644 --- a/init/devices.c +++ b/init/devices.c @@ -31,20 +31,25 @@ #include <private/android_filesystem_config.h> #include <sys/time.h> #include <asm/page.h> +#include <sys/wait.h> -#include "init.h" #include "devices.h" +#include "util.h" +#include "log.h" +#include "list.h" -#define CMDLINE_PREFIX "/dev" #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR "/etc/firmware" -#define MAX_QEMU_PERM 6 + +static int device_fd = -1; struct uevent { const char *action; const char *path; const char *subsystem; const char *firmware; + const char *partition_name; + int partition_num; int major; int minor; }; @@ -81,103 +86,21 @@ struct perms_ { unsigned int gid; unsigned short prefix; }; -static struct perms_ devperms[] = { - { "/dev/null", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/zero", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/full", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/ptmx", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/tty", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/random", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/urandom", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/ashmem", 0666, AID_ROOT, AID_ROOT, 0 }, - { "/dev/binder", 0666, AID_ROOT, AID_ROOT, 0 }, - - /* logger should be world writable (for logging) but not readable */ - { "/dev/log/", 0662, AID_ROOT, AID_LOG, 1 }, - - /* the msm hw3d client device node is world writable/readable. */ - { "/dev/msm_hw3dc", 0666, AID_ROOT, AID_ROOT, 0 }, - - /* gpu driver for adreno200 is globally accessible */ - { "/dev/kgsl", 0666, AID_ROOT, AID_ROOT, 0 }, - - /* these should not be world writable */ - { "/dev/diag", 0660, AID_RADIO, AID_RADIO, 0 }, - { "/dev/diag_arm9", 0660, AID_RADIO, AID_RADIO, 0 }, - { "/dev/android_adb", 0660, AID_ADB, AID_ADB, 0 }, - { "/dev/android_adb_enable", 0660, AID_ADB, AID_ADB, 0 }, - { "/dev/ttyMSM0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, - { "/dev/ttyHS0", 0600, AID_BLUETOOTH, AID_BLUETOOTH, 0 }, - { "/dev/uinput", 0660, AID_SYSTEM, AID_BLUETOOTH, 0 }, - { "/dev/alarm", 0664, AID_SYSTEM, AID_RADIO, 0 }, - { "/dev/tty0", 0660, AID_ROOT, AID_SYSTEM, 0 }, - { "/dev/graphics/", 0660, AID_ROOT, AID_GRAPHICS, 1 }, - { "/dev/msm_hw3dm", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, - { "/dev/input/", 0660, AID_ROOT, AID_INPUT, 1 }, - { "/dev/eac", 0660, AID_ROOT, AID_AUDIO, 0 }, - { "/dev/cam", 0660, AID_ROOT, AID_CAMERA, 0 }, - { "/dev/pmem", 0660, AID_SYSTEM, AID_GRAPHICS, 0 }, - { "/dev/pmem_adsp", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/pmem_camera", 0660, AID_SYSTEM, AID_CAMERA, 1 }, - { "/dev/oncrpc/", 0660, AID_ROOT, AID_SYSTEM, 1 }, - { "/dev/adsp/", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/snd/", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/mt9t013", 0660, AID_SYSTEM, AID_SYSTEM, 0 }, - { "/dev/msm_camera/", 0660, AID_SYSTEM, AID_SYSTEM, 1 }, - { "/dev/akm8976_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8976_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8973_daemon",0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8973_aot", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/bma150", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/cm3602", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/akm8976_pffd", 0640, AID_COMPASS, AID_SYSTEM, 0 }, - { "/dev/lightsensor", 0640, AID_SYSTEM, AID_SYSTEM, 0 }, - { "/dev/msm_pcm_out", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_pcm_in", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_pcm_ctl", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_snd", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_mp3", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/audience_a1026", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/tpa2018d1", 0660, AID_SYSTEM, AID_AUDIO, 1 }, - { "/dev/msm_audpre", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/msm_audio_ctl", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/htc-acoustic", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/vdec", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/q6venc", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/snd/dsp", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/snd/dsp1", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/snd/mixer", 0660, AID_SYSTEM, AID_AUDIO, 0 }, - { "/dev/smd0", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qemu_trace", 0666, AID_SYSTEM, AID_SYSTEM, 0 }, - { "/dev/qmi", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi0", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi1", 0640, AID_RADIO, AID_RADIO, 0 }, - { "/dev/qmi2", 0640, AID_RADIO, AID_RADIO, 0 }, - /* CDMA radio interface MUX */ - { "/dev/ts0710mux", 0640, AID_RADIO, AID_RADIO, 1 }, - { "/dev/ppp", 0660, AID_RADIO, AID_VPN, 0 }, - { "/dev/tun", 0640, AID_VPN, AID_VPN, 0 }, - { "/dev/bus/usb/", 0660, AID_ROOT, AID_USB, 1 }, - { NULL, 0, 0, 0, 0 }, -}; -/* devperms_partners list and perm_node are for hardware specific /dev entries */ struct perm_node { struct perms_ dp; struct listnode plist; }; -list_declare(devperms_partners); +static list_declare(dev_perms); /* * Permission override when in emulator mode, must be parsed before * system properties is initalized. */ -static int qemu_perm_count; -static struct perms_ qemu_perms[MAX_QEMU_PERM + 1]; - -int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix) { +int add_dev_perms(const char *name, mode_t perm, unsigned int uid, + unsigned int gid, unsigned short prefix) { int size; + char *tmp = 0; struct perm_node *node = malloc(sizeof (struct perm_node)); if (!node) return -ENOMEM; @@ -192,51 +115,10 @@ int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, node->dp.gid = gid; node->dp.prefix = prefix; - list_add_tail(&devperms_partners, &node->plist); + list_add_tail(&dev_perms, &node->plist); return 0; } -void qemu_init(void) { - qemu_perm_count = 0; - memset(&qemu_perms, 0, sizeof(qemu_perms)); -} - -static int qemu_perm(const char* name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix) -{ - char *buf; - if (qemu_perm_count == MAX_QEMU_PERM) - return -ENOSPC; - - buf = malloc(strlen(name) + 1); - if (!buf) - return -errno; - - strlcpy(buf, name, strlen(name) + 1); - qemu_perms[qemu_perm_count].name = buf; - qemu_perms[qemu_perm_count].perm = perm; - qemu_perms[qemu_perm_count].uid = uid; - qemu_perms[qemu_perm_count].gid = gid; - qemu_perms[qemu_perm_count].prefix = prefix; - - qemu_perm_count++; - return 0; -} - -/* Permission overrides for emulator that are parsed from /proc/cmdline. */ -void qemu_cmdline(const char* name, const char *value) -{ - char *buf; - if (!strcmp(name, "android.ril")) { - /* cmd line params currently assume /dev/ prefix */ - if (asprintf(&buf, CMDLINE_PREFIX"/%s", value) == -1) { - return; - } - INFO("nani- buf:: %s\n", buf); - qemu_perm(buf, 0660, AID_RADIO, AID_ROOT, 0); - } -} - static int get_device_perm_inner(struct perms_ *perms, const char *path, unsigned *uid, unsigned *gid, mode_t *perm) { @@ -262,38 +144,32 @@ static int get_device_perm_inner(struct perms_ *perms, const char *path, static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) { mode_t perm; - - if (get_device_perm_inner(qemu_perms, path, uid, gid, &perm) == 0) { - return perm; - } else if (get_device_perm_inner(devperms, path, uid, gid, &perm) == 0) { - return perm; - } else { - struct listnode *node; - struct perm_node *perm_node; - struct perms_ *dp; - - /* Check partners list. */ - list_for_each(node, &devperms_partners) { - perm_node = node_to_item(node, struct perm_node, plist); - dp = &perm_node->dp; - - if (dp->prefix) { - if (strncmp(path, dp->name, strlen(dp->name))) - continue; - } else { - if (strcmp(path, dp->name)) - continue; - } - /* Found perm in partner list. */ - *uid = dp->uid; - *gid = dp->gid; - return dp->perm; + struct listnode *node; + struct perm_node *perm_node; + struct perms_ *dp; + + /* search the perms list in reverse so that ueventd.$hardware can + * override ueventd.rc + */ + list_for_each_reverse(node, &dev_perms) { + perm_node = node_to_item(node, struct perm_node, plist); + dp = &perm_node->dp; + + if (dp->prefix) { + if (strncmp(path, dp->name, strlen(dp->name))) + continue; + } else { + if (strcmp(path, dp->name)) + continue; } - /* Default if nothing found. */ - *uid = 0; - *gid = 0; - return 0600; + *uid = dp->uid; + *gid = dp->gid; + return dp->perm; } + /* Default if nothing found. */ + *uid = 0; + *gid = 0; + return 0600; } static void make_device(const char *path, int block, int major, int minor) @@ -345,6 +221,8 @@ static void parse_event(const char *msg, struct uevent *uevent) uevent->firmware = ""; uevent->major = -1; uevent->minor = -1; + uevent->partition_name = NULL; + uevent->partition_num = -1; /* currently ignoring SEQNUM */ while(*msg) { @@ -366,6 +244,12 @@ static void parse_event(const char *msg, struct uevent *uevent) } else if(!strncmp(msg, "MINOR=", 6)) { msg += 6; uevent->minor = atoi(msg); + } else if(!strncmp(msg, "PARTN=", 6)) { + msg += 6; + uevent->partition_num = atoi(msg); + } else if(!strncmp(msg, "PARTNAME=", 9)) { + msg += 9; + uevent->partition_name = msg; } /* advance to after the next \0 */ @@ -378,12 +262,77 @@ static void parse_event(const char *msg, struct uevent *uevent) uevent->firmware, uevent->major, uevent->minor); } +static char **parse_platform_block_device(struct uevent *uevent) +{ + const char *driver; + const char *path; + char *slash; + int width; + char buf[256]; + char link_path[256]; + int fd; + int link_num = 0; + int ret; + char *p; + unsigned int size; + struct stat info; + + char **links = malloc(sizeof(char *) * 4); + if (!links) + return NULL; + memset(links, 0, sizeof(char *) * 4); + + /* Drop "/devices/platform/" */ + path = uevent->path; + driver = path + 18; + slash = strchr(driver, '/'); + if (!slash) + goto err; + width = slash - driver; + if (width <= 0) + goto err; + + snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s", + width, driver); + + if (uevent->partition_name) { + p = strdup(uevent->partition_name); + sanitize(p); + if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0) + link_num++; + else + links[link_num] = NULL; + free(p); + } + + if (uevent->partition_num >= 0) { + if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0) + link_num++; + else + links[link_num] = NULL; + } + + slash = strrchr(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_event(struct uevent *uevent) { char devpath[96]; int devpath_ready = 0; char *base, *name; + char **links = NULL; int block; + int i; /* if it's not a /dev device, nothing to do */ if((uevent->major < 0) || (uevent->minor < 0)) @@ -404,6 +353,8 @@ static void handle_device_event(struct uevent *uevent) block = 1; base = "/dev/block/"; mkdir(base, 0755); + if (!strncmp(uevent->path, "/devices/platform/", 18)) + links = parse_platform_block_device(uevent); } else { block = 0; /* this should probably be configurable somehow */ @@ -461,12 +412,24 @@ static void handle_device_event(struct uevent *uevent) if(!strcmp(uevent->action, "add")) { make_device(devpath, block, uevent->major, uevent->minor); - return; + if (links) { + for (i = 0; links[i]; i++) + make_link(devpath, links[i]); + } } if(!strcmp(uevent->action, "remove")) { + if (links) { + for (i = 0; links[i]; i++) + remove_link(devpath, links[i]); + } unlink(devpath); - return; + } + + if (links) { + for (i = 0; links[i]; i++) + free(links[i]); + free(links); } } @@ -575,6 +538,8 @@ root_free_out: static void handle_firmware_event(struct uevent *uevent) { pid_t pid; + int status; + int ret; if(strcmp(uevent->subsystem, "firmware")) return; @@ -587,16 +552,20 @@ static void handle_firmware_event(struct uevent *uevent) if (!pid) { process_firmware_event(uevent); exit(EXIT_SUCCESS); + } else { + do { + ret = waitpid(pid, &status, 0); + } while (ret == -1 && errno == EINTR); } } #define UEVENT_MSG_LEN 1024 -void handle_device_fd(int fd) +void handle_device_fd() { char msg[UEVENT_MSG_LEN+2]; int n; - while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) { + while((n = recv(device_fd, msg, UEVENT_MSG_LEN, 0)) > 0) { struct uevent uevent; if(n == UEVENT_MSG_LEN) /* overflow -- discard */ @@ -621,7 +590,7 @@ void handle_device_fd(int fd) ** socket's buffer. */ -static void do_coldboot(int event_fd, DIR *d) +static void do_coldboot(DIR *d) { struct dirent *de; int dfd, fd; @@ -632,7 +601,7 @@ static void do_coldboot(int event_fd, DIR *d) if(fd >= 0) { write(fd, "add\n", 4); close(fd); - handle_device_fd(event_fd); + handle_device_fd(); } while((de = readdir(d))) { @@ -649,40 +618,49 @@ static void do_coldboot(int event_fd, DIR *d) if(d2 == 0) close(fd); else { - do_coldboot(event_fd, d2); + do_coldboot(d2); closedir(d2); } } } -static void coldboot(int event_fd, const char *path) +static void coldboot(const char *path) { DIR *d = opendir(path); if(d) { - do_coldboot(event_fd, d); + do_coldboot(d); closedir(d); } } -int device_init(void) +void device_init(void) { suseconds_t t0, t1; + struct stat info; int fd; - fd = open_uevent_socket(); - if(fd < 0) - return -1; - - fcntl(fd, F_SETFD, FD_CLOEXEC); - fcntl(fd, F_SETFL, O_NONBLOCK); + device_fd = open_uevent_socket(); + if(device_fd < 0) + return; - t0 = get_usecs(); - coldboot(fd, "/sys/class"); - coldboot(fd, "/sys/block"); - coldboot(fd, "/sys/devices"); - t1 = get_usecs(); + fcntl(device_fd, F_SETFD, FD_CLOEXEC); + fcntl(device_fd, F_SETFL, O_NONBLOCK); - log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); + if (stat(coldboot_done, &info) < 0) { + t0 = get_usecs(); + coldboot("/sys/class"); + coldboot("/sys/block"); + coldboot("/sys/devices"); + t1 = get_usecs(); + fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000); + close(fd); + log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); + } else { + log_event_print("skipping coldboot, already done\n"); + } +} - return fd; +int get_device_fd() +{ + return device_fd; } diff --git a/init/devices.h b/init/devices.h index b484da4..8593a1b 100644 --- a/init/devices.h +++ b/init/devices.h @@ -17,11 +17,11 @@ #ifndef _INIT_DEVICES_H #define _INIT_DEVICES_H -extern void handle_device_fd(int fd); -extern int device_init(void); -extern void qemu_init(void); -extern void qemu_cmdline(const char* name, const char *value); -extern int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, - unsigned int gid, unsigned short prefix); +#include <sys/stat.h> +extern void handle_device_fd(); +extern void device_init(void); +extern int add_dev_perms(const char *name, mode_t perm, unsigned int uid, + unsigned int gid, unsigned short prefix); +int get_device_fd(); #endif /* _INIT_DEVICES_H */ diff --git a/init/init.c b/init/init.c index 4d98cc2..8f95da7 100755 --- a/init/init.c +++ b/init/init.c @@ -25,27 +25,32 @@ #include <sys/mount.h> #include <sys/stat.h> #include <sys/poll.h> -#include <time.h> #include <errno.h> #include <stdarg.h> #include <mtd/mtd-user.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> -#include <sys/reboot.h> +#include <libgen.h> #include <cutils/sockets.h> #include <cutils/iosched_policy.h> +#include <private/android_filesystem_config.h> #include <termios.h> -#include <linux/kd.h> -#include <linux/keychord.h> #include <sys/system_properties.h> #include "devices.h" #include "init.h" +#include "list.h" +#include "log.h" #include "property_service.h" #include "bootchart.h" +#include "signal_handler.h" +#include "keychords.h" +#include "init_parser.h" +#include "util.h" +#include "ueventd.h" static int property_triggers_enabled = 0; @@ -62,11 +67,12 @@ static char bootloader[32]; static char hardware[32]; static unsigned revision = 0; static char qemu[32]; -static struct input_keychord *keychords = 0; -static int keychords_count = 0; -static int keychords_length = 0; -static void notify_service_state(const char *name, const char *state) +static struct action *cur_action = NULL; +static struct command *cur_command = NULL; +static struct listnode *command_queue = NULL; + +void notify_service_state(const char *name, const char *state) { char pname[PROP_NAME_MAX]; int len = strlen(name); @@ -122,24 +128,6 @@ static void open_console() close(fd); } -/* - * gettime() - returns the time in seconds of the system's monotonic clock or - * zero on error. - */ -static time_t gettime(void) -{ - struct timespec ts; - int ret; - - ret = clock_gettime(CLOCK_MONOTONIC, &ts); - if (ret < 0) { - ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); - return 0; - } - - return ts.tv_sec; -} - static void publish_socket(const char *name, int fd) { char key[64] = ANDROID_SOCKET_ENV_PREFIX; @@ -208,9 +196,11 @@ void service_start(struct service *svc, const char *dynamic_args) char tmp[32]; int fd, sz; - get_property_workspace(&fd, &sz); - sprintf(tmp, "%d,%d", dup(fd), sz); - add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); + if (properties_inited()) { + get_property_workspace(&fd, &sz); + sprintf(tmp, "%d,%d", dup(fd), sz); + add_environment("ANDROID_PROPERTY_WORKSPACE", tmp); + } for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); @@ -266,7 +256,7 @@ void service_start(struct service *svc, const char *dynamic_args) ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno)); } } else { - char *arg_ptrs[SVC_MAXARGS+1]; + char *arg_ptrs[INIT_PARSER_MAXARGS+1]; int arg_idx = svc->nargs; char *tmp = strdup(dynamic_args); char *next = tmp; @@ -277,7 +267,7 @@ void service_start(struct service *svc, const char *dynamic_args) while((bword = strsep(&next, " "))) { arg_ptrs[arg_idx++] = bword; - if (arg_idx == SVC_MAXARGS) + if (arg_idx == INIT_PARSER_MAXARGS) break; } arg_ptrs[arg_idx] = '\0'; @@ -296,7 +286,8 @@ void service_start(struct service *svc, const char *dynamic_args) svc->pid = pid; svc->flags |= SVC_RUNNING; - notify_service_state(svc->name, "running"); + if (properties_inited()) + notify_service_state(svc->name, "running"); } void service_stop(struct service *svc) @@ -322,90 +313,8 @@ void service_stop(struct service *svc) void property_changed(const char *name, const char *value) { - if (property_triggers_enabled) { + if (property_triggers_enabled) queue_property_triggers(name, value); - drain_action_queue(); - } -} - -#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */ -#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/ - -static int wait_for_one_process(int block) -{ - pid_t pid; - int status; - struct service *svc; - struct socketinfo *si; - time_t now; - struct listnode *node; - struct command *cmd; - - while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); - if (pid <= 0) return -1; - INFO("waitpid returned pid %d, status = %08x\n", pid, status); - - svc = service_find_by_pid(pid); - if (!svc) { - ERROR("untracked pid %d exited\n", pid); - return 0; - } - - NOTICE("process '%s', pid %d exited\n", svc->name, pid); - - if (!(svc->flags & SVC_ONESHOT)) { - kill(-pid, SIGKILL); - NOTICE("process '%s' killing any children in process group\n", svc->name); - } - - /* remove any sockets we may have created */ - for (si = svc->sockets; si; si = si->next) { - char tmp[128]; - snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); - unlink(tmp); - } - - svc->pid = 0; - svc->flags &= (~SVC_RUNNING); - - /* oneshot processes go into the disabled state on exit */ - if (svc->flags & SVC_ONESHOT) { - svc->flags |= SVC_DISABLED; - } - - /* disabled processes do not get restarted automatically */ - if (svc->flags & SVC_DISABLED) { - notify_service_state(svc->name, "stopped"); - return 0; - } - - now = gettime(); - if (svc->flags & SVC_CRITICAL) { - if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { - if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { - ERROR("critical process '%s' exited %d times in %d minutes; " - "rebooting into recovery mode\n", svc->name, - CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); - sync(); - __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, - LINUX_REBOOT_CMD_RESTART2, "recovery"); - return 0; - } - } else { - svc->time_crashed = now; - svc->nr_crashed = 1; - } - } - - svc->flags |= SVC_RESTARTING; - - /* Execute all onrestart commands for this service. */ - list_for_each(node, &svc->onrestart.commands) { - cmd = node_to_item(node, struct command, clist); - cmd->func(cmd->nargs, cmd->args); - } - notify_service_state(svc->name, "restarting"); - return 0; } static void restart_service_if_needed(struct service *svc) @@ -431,13 +340,6 @@ static void restart_processes() restart_service_if_needed); } -static int signal_fd = -1; - -static void sigchld_handler(int s) -{ - write(signal_fd, &s, 1); -} - static void msg_start(const char *name) { struct service *svc; @@ -486,78 +388,6 @@ void handle_control_message(const char *msg, const char *arg) } } -#define MAX_MTD_PARTITIONS 16 - -static struct { - char name[16]; - int number; -} mtd_part_map[MAX_MTD_PARTITIONS]; - -static int mtd_part_count = -1; - -static void find_mtd_partitions(void) -{ - int fd; - char buf[1024]; - char *pmtdbufp; - ssize_t pmtdsize; - int r; - - fd = open("/proc/mtd", O_RDONLY); - if (fd < 0) - return; - - buf[sizeof(buf) - 1] = '\0'; - pmtdsize = read(fd, buf, sizeof(buf) - 1); - pmtdbufp = buf; - while (pmtdsize > 0) { - int mtdnum, mtdsize, mtderasesize; - char mtdname[16]; - mtdname[0] = '\0'; - mtdnum = -1; - r = sscanf(pmtdbufp, "mtd%d: %x %x %15s", - &mtdnum, &mtdsize, &mtderasesize, mtdname); - if ((r == 4) && (mtdname[0] == '"')) { - char *x = strchr(mtdname + 1, '"'); - if (x) { - *x = 0; - } - INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1); - if (mtd_part_count < MAX_MTD_PARTITIONS) { - strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1); - mtd_part_map[mtd_part_count].number = mtdnum; - mtd_part_count++; - } else { - ERROR("too many mtd partitions\n"); - } - } - while (pmtdsize > 0 && *pmtdbufp != '\n') { - pmtdbufp++; - pmtdsize--; - } - if (pmtdsize > 0) { - pmtdbufp++; - pmtdsize--; - } - } - close(fd); -} - -int mtd_name_to_number(const char *name) -{ - int n; - if (mtd_part_count < 0) { - mtd_part_count = 0; - find_mtd_partitions(); - } - for (n = 0; n < mtd_part_count; n++) { - if (!strcmp(name, mtd_part_map[n].name)) { - return mtd_part_map[n].number; - } - } - return -1; -} - static void import_kernel_nv(char *name, int in_qemu) { char *value = strchr(name, '='); @@ -585,8 +415,6 @@ static void import_kernel_nv(char *name, int in_qemu) strlcpy(bootloader, value, sizeof(bootloader)); } else if (!strcmp(name,"androidboot.hardware")) { strlcpy(hardware, value, sizeof(hardware)); - } else { - qemu_cmdline(name, value); } } else { /* in the emulator, export any kernel option with the @@ -631,198 +459,208 @@ static void import_kernel_cmdline(int in_qemu) chmod("/proc/cmdline", 0440); } -static void get_hardware_name(void) +static struct command *get_first_command(struct action *act) { - char data[1024]; - int fd, n; - char *x, *hw, *rev; + struct listnode *node; + node = list_head(&act->commands); + if (!node) + return NULL; - /* Hardware string was provided on kernel command line */ - if (hardware[0]) - return; + return node_to_item(node, struct command, clist); +} - fd = open("/proc/cpuinfo", O_RDONLY); - if (fd < 0) return; +static struct command *get_next_command(struct action *act, struct command *cmd) +{ + struct listnode *node; + node = cmd->clist.next; + if (!node) + return NULL; + if (node == &act->commands) + return NULL; - n = read(fd, data, 1023); - close(fd); - if (n < 0) return; - - data[n] = 0; - hw = strstr(data, "\nHardware"); - rev = strstr(data, "\nRevision"); - - if (hw) { - x = strstr(hw, ": "); - if (x) { - x += 2; - n = 0; - while (*x && !isspace(*x)) { - hardware[n++] = tolower(*x); - x++; - if (n == 31) break; - } - hardware[n] = 0; - } - } + return node_to_item(node, struct command, clist); +} - if (rev) { - x = strstr(rev, ": "); - if (x) { - revision = strtoul(x + 2, 0, 16); - } - } +static int is_last_command(struct action *act, struct command *cmd) +{ + return (list_tail(&act->commands) == &cmd->clist); } -void drain_action_queue(void) +void execute_one_command(void) { - struct listnode *node; - struct command *cmd; - struct action *act; int ret; - while ((act = action_remove_queue_head())) { - INFO("processing action %p (%s)\n", act, act->name); - list_for_each(node, &act->commands) { - cmd = node_to_item(node, struct command, clist); - ret = cmd->func(cmd->nargs, cmd->args); - INFO("command '%s' r=%d\n", cmd->args[0], ret); - } + if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) { + cur_action = action_remove_queue_head(); + cur_command = NULL; + if (!cur_action) + return; + INFO("processing action %p (%s)\n", cur_action, cur_action->name); + cur_command = get_first_command(cur_action); + } else { + cur_command = get_next_command(cur_action, cur_command); } + + if (!cur_command) + return; + + ret = cur_command->func(cur_command->nargs, cur_command->args); + INFO("command '%s' r=%d\n", cur_command->args[0], ret); } -void open_devnull_stdio(void) +static int wait_for_coldboot_done_action(int nargs, char **args) { - int fd; - static const char *name = "/dev/__null__"; - if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) { - fd = open(name, O_RDWR); - unlink(name); - if (fd >= 0) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) { - close(fd); - } - return; - } - } + int ret; + INFO("wait for %s\n", coldboot_done); + ret = wait_for_file(coldboot_done, COMMAND_RETRY_TIMEOUT); + if (ret) + ERROR("Timed out waiting for %s\n", coldboot_done); + return ret; +} - exit(1); +static int property_init_action(int nargs, char **args) +{ + INFO("property init\n"); + property_init(); + return 0; } -void add_service_keycodes(struct service *svc) +static int keychord_init_action(int nargs, char **args) { - struct input_keychord *keychord; - int i, size; - - if (svc->keycodes) { - /* add a new keychord to the list */ - size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]); - keychords = realloc(keychords, keychords_length + size); - if (!keychords) { - ERROR("could not allocate keychords\n"); - keychords_length = 0; - keychords_count = 0; - return; - } + keychord_init(); + return 0; +} - keychord = (struct input_keychord *)((char *)keychords + keychords_length); - keychord->version = KEYCHORD_VERSION; - keychord->id = keychords_count + 1; - keychord->count = svc->nkeycodes; - svc->keychord_id = keychord->id; +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); + } - for (i = 0; i < svc->nkeycodes; i++) { - keychord->keycodes[i] = svc->keycodes[i]; + fd = open(console_name, O_RDWR); + if (fd >= 0) + have_console = 1; + close(fd); + + if( load_565rle_image(INIT_IMAGE_FILE) ) { + fd = open("/dev/tty0", O_WRONLY); + if (fd >= 0) { + const char *msg; + msg = "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" // console is 40 cols x 30 lines + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + " A N D R O I D "; + write(fd, msg, strlen(msg)); + close(fd); } - keychords_count++; - keychords_length += size; } + return 0; } -int open_keychord() +static int set_init_properties_action(int nargs, char **args) { - int fd, ret; + char tmp[PROP_VALUE_MAX]; - service_for_each(add_service_keycodes); - - /* nothing to do if no services require keychords */ - if (!keychords) - return -1; - - fd = open("/dev/keychord", O_RDWR); - if (fd < 0) { - ERROR("could not open /dev/keychord\n"); - return fd; - } - fcntl(fd, F_SETFD, FD_CLOEXEC); + if (qemu[0]) + import_kernel_cmdline(1); - ret = write(fd, keychords, keychords_length); - if (ret != keychords_length) { - ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno); - close(fd); - fd = -1; - } + if (!strcmp(bootmode,"factory")) + property_set("ro.factorytest", "1"); + else if (!strcmp(bootmode,"factory2")) + property_set("ro.factorytest", "2"); + else + property_set("ro.factorytest", "0"); - free(keychords); - keychords = 0; + property_set("ro.serialno", serialno[0] ? serialno : ""); + property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown"); + property_set("ro.baseband", baseband[0] ? baseband : "unknown"); + property_set("ro.carrier", carrier[0] ? carrier : "unknown"); + property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown"); - return fd; + property_set("ro.hardware", hardware); + snprintf(tmp, PROP_VALUE_MAX, "%d", revision); + property_set("ro.revision", tmp); + return 0; } -void handle_keychord(int fd) +static int property_service_init_action(int nargs, char **args) { - struct service *svc; - char* debuggable; - char* adb_enabled; - 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"); - if ((debuggable && !strcmp(debuggable, "1")) || - (adb_enabled && !strcmp(adb_enabled, "running"))) { - ret = read(fd, &id, sizeof(id)); - if (ret != sizeof(id)) { - ERROR("could not read keychord id\n"); - return; - } + /* read any property files on system or data and + * fire up the property service. This must happen + * after the ro.foo properties are set above so + * that /data/local.prop cannot interfere with them. + */ + start_property_service(); + return 0; +} - svc = service_find_by_keychord(id); - if (svc) { - INFO("starting service %s from keychord\n", svc->name); - service_start(svc, NULL); - } else { - ERROR("service for keychord %d not found\n", id); - } +static int signal_init_action(int nargs, char **args) +{ + signal_init(); + return 0; +} + +static int check_startup_action(int nargs, char **args) +{ + /* make sure we actually have all the pieces we need */ + if ((get_property_set_fd() < 0) || + (get_signal_fd() < 0)) { + ERROR("init startup failure\n"); + exit(1); } + return 0; +} + +static int queue_property_triggers_action(int nargs, char **args) +{ + queue_all_property_triggers(); + /* enable property triggers */ + property_triggers_enabled = 1; + return 0; } +#if BOOTCHART +static int bootchart_init_action(int nargs, char **args) +{ + bootchart_count = bootchart_init(); + if (bootchart_count < 0) { + ERROR("bootcharting init failure\n"); + } else if (bootchart_count > 0) { + NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); + } else { + NOTICE("bootcharting ignored\n"); + } +} +#endif + int main(int argc, char **argv) { - int device_fd = -1; - int property_set_fd = -1; - int signal_recv_fd = -1; - int keychord_fd = -1; - int fd_count; - int s[2]; - int fd; - struct sigaction act; - char tmp[PROP_VALUE_MAX]; + int fd_count = 0; struct pollfd ufds[4]; char *tmpdev; char* debuggable; + char tmp[32]; + int property_set_fd_init = 0; + int signal_fd_init = 0; + int keychord_fd_init = 0; - act.sa_handler = sigchld_handler; - act.sa_flags = SA_NOCLDSTOP; - act.sa_mask = 0; - act.sa_restorer = NULL; - sigaction(SIGCHLD, &act, 0); + if (!strcmp(basename(argv[0]), "ueventd")) + return ueventd_main(argc, argv); /* clear the umask */ umask(0); @@ -852,165 +690,82 @@ int main(int argc, char **argv) log_init(); INFO("reading config file\n"); - parse_config_file("/init.rc"); + init_parse_config_file("/init.rc"); /* pull the kernel commandline and ramdisk properties file in */ - qemu_init(); import_kernel_cmdline(0); - get_hardware_name(); + get_hardware_name(hardware, &revision); snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware); - parse_config_file(tmp); + init_parse_config_file(tmp); action_for_each_trigger("early-init", action_add_queue_tail); - drain_action_queue(); - - INFO("device init\n"); - device_fd = device_init(); - - property_init(); - - // only listen for keychords if ro.debuggable is true - keychord_fd = open_keychord(); - - if (console[0]) { - snprintf(tmp, sizeof(tmp), "/dev/%s", console); - console_name = strdup(tmp); - } - - fd = open(console_name, O_RDWR); - if (fd >= 0) - have_console = 1; - close(fd); - - if( load_565rle_image(INIT_IMAGE_FILE) ) { - fd = open("/dev/tty0", O_WRONLY); - if (fd >= 0) { - const char *msg; - msg = "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" // console is 40 cols x 30 lines - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - " A N D R O I D "; - write(fd, msg, strlen(msg)); - close(fd); - } - } - if (qemu[0]) - import_kernel_cmdline(1); - - if (!strcmp(bootmode,"factory")) - property_set("ro.factorytest", "1"); - else if (!strcmp(bootmode,"factory2")) - property_set("ro.factorytest", "2"); - else - property_set("ro.factorytest", "0"); - - property_set("ro.serialno", serialno[0] ? serialno : ""); - property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown"); - property_set("ro.baseband", baseband[0] ? baseband : "unknown"); - property_set("ro.carrier", carrier[0] ? carrier : "unknown"); - property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown"); - - property_set("ro.hardware", hardware); - snprintf(tmp, PROP_VALUE_MAX, "%d", revision); - property_set("ro.revision", tmp); + queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); + queue_builtin_action(property_init_action, "property_init"); + queue_builtin_action(keychord_init_action, "keychord_init"); + queue_builtin_action(console_init_action, "console_init"); + queue_builtin_action(set_init_properties_action, "set_init_properties"); /* execute all the boot actions to get us started */ action_for_each_trigger("init", action_add_queue_tail); - drain_action_queue(); - - /* read any property files on system or data and - * fire up the property service. This must happen - * after the ro.foo properties are set above so - * that /data/local.prop cannot interfere with them. - */ - property_set_fd = start_property_service(); - - /* create a signalling mechanism for the sigchld handler */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { - signal_fd = s[0]; - signal_recv_fd = s[1]; - fcntl(s[0], F_SETFD, FD_CLOEXEC); - fcntl(s[0], F_SETFL, O_NONBLOCK); - fcntl(s[1], F_SETFD, FD_CLOEXEC); - fcntl(s[1], F_SETFL, O_NONBLOCK); - } + action_for_each_trigger("early-fs", action_add_queue_tail); + action_for_each_trigger("fs", action_add_queue_tail); + action_for_each_trigger("post-fs", action_add_queue_tail); - /* make sure we actually have all the pieces we need */ - if ((device_fd < 0) || - (property_set_fd < 0) || - (signal_recv_fd < 0)) { - ERROR("init startup failure\n"); - return 1; - } + queue_builtin_action(property_service_init_action, "property_service_init"); + queue_builtin_action(signal_init_action, "signal_init"); + queue_builtin_action(check_startup_action, "check_startup"); /* execute all the boot actions to get us started */ action_for_each_trigger("early-boot", action_add_queue_tail); action_for_each_trigger("boot", action_add_queue_tail); - drain_action_queue(); /* run all property triggers based on current state of the properties */ - queue_all_property_triggers(); - drain_action_queue(); - - /* enable property triggers */ - property_triggers_enabled = 1; - - ufds[0].fd = device_fd; - ufds[0].events = POLLIN; - ufds[1].fd = property_set_fd; - ufds[1].events = POLLIN; - ufds[2].fd = signal_recv_fd; - ufds[2].events = POLLIN; - fd_count = 3; - - if (keychord_fd > 0) { - ufds[3].fd = keychord_fd; - ufds[3].events = POLLIN; - fd_count++; - } else { - ufds[3].events = 0; - ufds[3].revents = 0; - } + queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers"); + #if BOOTCHART - bootchart_count = bootchart_init(); - if (bootchart_count < 0) { - ERROR("bootcharting init failure\n"); - } else if (bootchart_count > 0) { - NOTICE("bootcharting started (period=%d ms)\n", bootchart_count*BOOTCHART_POLLING_MS); - } else { - NOTICE("bootcharting ignored\n"); - } + queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif for(;;) { int nr, i, timeout = -1; - for (i = 0; i < fd_count; i++) - ufds[i].revents = 0; - - drain_action_queue(); + execute_one_command(); restart_processes(); + if (!property_set_fd_init && get_property_set_fd() > 0) { + ufds[fd_count].fd = get_property_set_fd(); + ufds[fd_count].events = POLLIN; + ufds[fd_count].revents = 0; + fd_count++; + property_set_fd_init = 1; + } + if (!signal_fd_init && get_signal_fd() > 0) { + ufds[fd_count].fd = get_signal_fd(); + ufds[fd_count].events = POLLIN; + ufds[fd_count].revents = 0; + fd_count++; + signal_fd_init = 1; + } + if (!keychord_fd_init && get_keychord_fd() > 0) { + ufds[fd_count].fd = get_keychord_fd(); + ufds[fd_count].events = POLLIN; + ufds[fd_count].revents = 0; + fd_count++; + keychord_fd_init = 1; + } + if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } + if (!action_queue_empty() || cur_action) + timeout = 0; + #if BOOTCHART if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) @@ -1021,25 +776,21 @@ int main(int argc, char **argv) } } #endif + nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; - if (ufds[2].revents == POLLIN) { - /* we got a SIGCHLD - reap and restart as needed */ - read(signal_recv_fd, tmp, sizeof(tmp)); - while (!wait_for_one_process(0)) - ; - continue; + for (i = 0; i < fd_count; i++) { + if (ufds[i].revents == POLLIN) { + if (ufds[i].fd == get_property_set_fd()) + handle_property_set_fd(); + else if (ufds[i].fd == get_keychord_fd()) + handle_keychord(); + else if (ufds[i].fd == get_signal_fd()) + handle_signal(); + } } - - if (ufds[0].revents == POLLIN) - handle_device_fd(device_fd); - - if (ufds[1].revents == POLLIN) - handle_property_set_fd(property_set_fd); - if (ufds[3].revents == POLLIN) - handle_keychord(keychord_fd); } return 0; diff --git a/init/init.h b/init/init.h index f92a4d7..8691335 100644 --- a/init/init.h +++ b/init/init.h @@ -17,55 +17,11 @@ #ifndef _INIT_INIT_H #define _INIT_INIT_H -int mtd_name_to_number(const char *name); +#include "list.h" -void handle_control_message(const char *msg, const char *arg); - -int create_socket(const char *name, int type, mode_t perm, - uid_t uid, gid_t gid); - -void *read_file(const char *fn, unsigned *_sz); - -void log_init(void); -void log_set_level(int level); -void log_close(void); -void log_write(int level, const char *fmt, ...) - __attribute__ ((format(printf, 2, 3))); - -#define ERROR(x...) log_write(3, "<3>init: " x) -#define NOTICE(x...) log_write(5, "<5>init: " x) -#define INFO(x...) log_write(6, "<6>init: " x) - -#define LOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ -#define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ - -unsigned int decode_uid(const char *s); - -struct listnode -{ - struct listnode *next; - struct listnode *prev; -}; - -#define node_to_item(node, container, member) \ - (container *) (((char*) (node)) - offsetof(container, member)) - -#define list_declare(name) \ - struct listnode name = { \ - .next = &name, \ - .prev = &name, \ - } +#include <sys/stat.h> -#define list_for_each(node, list) \ - for (node = (list)->next; node != (list); node = node->next) - -void list_init(struct listnode *list); -void list_add_tail(struct listnode *list, struct listnode *item); -void list_remove(struct listnode *item); - -#define list_empty(list) ((list) == (list)->next) -#define list_head(list) ((list)->next) -#define list_tail(list) ((list)->prev) +void handle_control_message(const char *msg, const char *arg); struct command { @@ -116,7 +72,7 @@ struct svcenvinfo { #define NR_SVC_SUPP_GIDS 12 /* twelve supplementary groups */ -#define SVC_MAXARGS 64 +#define COMMAND_RETRY_TIMEOUT 5 struct service { /* list of all services */ @@ -154,7 +110,7 @@ struct service { char *args[1]; }; /* ^-------'args' MUST be at the end of this struct! */ -int parse_config_file(const char *fn); +void notify_service_state(const char *name, const char *state); struct service *service_find_by_name(const char *name); struct service *service_find_by_pid(pid_t pid); @@ -168,14 +124,6 @@ void service_stop(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); -void drain_action_queue(void); -struct action *action_remove_queue_head(void); -void action_add_queue_tail(struct action *act); -void action_for_each_trigger(const char *trigger, - void (*func)(struct action *act)); -void queue_property_triggers(const char *name, const char *value); -void queue_all_property_triggers(); - #define INIT_IMAGE_FILE "/initlogo.rle" int load_565rle_image( char *file_name ); diff --git a/init/init_parser.c b/init/init_parser.c new file mode 100644 index 0000000..585a5b5 --- /dev/null +++ b/init/init_parser.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2010 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> +#include <stddef.h> +#include <ctype.h> + +#include "init.h" +#include "parser.h" +#include "init_parser.h" +#include "log.h" +#include "list.h" +#include "property_service.h" +#include "util.h" + +#include <cutils/iosched_policy.h> + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> + +static list_declare(service_list); +static list_declare(action_list); +static list_declare(action_queue); + +static void *parse_service(struct parse_state *state, int nargs, char **args); +static void parse_line_service(struct parse_state *state, int nargs, char **args); + +static void *parse_action(struct parse_state *state, int nargs, char **args); +static void parse_line_action(struct parse_state *state, int nargs, char **args); + +#define SECTION 0x01 +#define COMMAND 0x02 +#define OPTION 0x04 + +#include "keywords.h" + +#define KEYWORD(symbol, flags, nargs, func) \ + [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, + +struct { + const char *name; + int (*func)(int nargs, char **args); + unsigned char nargs; + unsigned char flags; +} keyword_info[KEYWORD_COUNT] = { + [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, +#include "keywords.h" +}; +#undef KEYWORD + +#define kw_is(kw, type) (keyword_info[kw].flags & (type)) +#define kw_name(kw) (keyword_info[kw].name) +#define kw_func(kw) (keyword_info[kw].func) +#define kw_nargs(kw) (keyword_info[kw].nargs) + +int lookup_keyword(const char *s) +{ + switch (*s++) { + case 'c': + if (!strcmp(s, "opy")) return K_copy; + if (!strcmp(s, "apability")) return K_capability; + if (!strcmp(s, "hdir")) return K_chdir; + if (!strcmp(s, "hroot")) return K_chroot; + if (!strcmp(s, "lass")) return K_class; + if (!strcmp(s, "lass_start")) return K_class_start; + if (!strcmp(s, "lass_stop")) return K_class_stop; + if (!strcmp(s, "onsole")) return K_console; + if (!strcmp(s, "hown")) return K_chown; + if (!strcmp(s, "hmod")) return K_chmod; + if (!strcmp(s, "ritical")) return K_critical; + break; + case 'd': + if (!strcmp(s, "isabled")) return K_disabled; + if (!strcmp(s, "omainname")) return K_domainname; + break; + case 'e': + if (!strcmp(s, "xec")) return K_exec; + if (!strcmp(s, "xport")) return K_export; + break; + case 'g': + if (!strcmp(s, "roup")) return K_group; + break; + case 'h': + if (!strcmp(s, "ostname")) return K_hostname; + break; + case 'i': + if (!strcmp(s, "oprio")) return K_ioprio; + if (!strcmp(s, "fup")) return K_ifup; + if (!strcmp(s, "nsmod")) return K_insmod; + if (!strcmp(s, "mport")) return K_import; + break; + case 'k': + if (!strcmp(s, "eycodes")) return K_keycodes; + break; + case 'l': + if (!strcmp(s, "oglevel")) return K_loglevel; + break; + case 'm': + if (!strcmp(s, "kdir")) return K_mkdir; + if (!strcmp(s, "ount")) return K_mount; + break; + case 'o': + if (!strcmp(s, "n")) return K_on; + if (!strcmp(s, "neshot")) return K_oneshot; + if (!strcmp(s, "nrestart")) return K_onrestart; + break; + case 'r': + if (!strcmp(s, "estart")) return K_restart; + break; + case 's': + if (!strcmp(s, "ervice")) return K_service; + if (!strcmp(s, "etenv")) return K_setenv; + if (!strcmp(s, "etkey")) return K_setkey; + if (!strcmp(s, "etprop")) return K_setprop; + if (!strcmp(s, "etrlimit")) return K_setrlimit; + if (!strcmp(s, "ocket")) return K_socket; + if (!strcmp(s, "tart")) return K_start; + if (!strcmp(s, "top")) return K_stop; + if (!strcmp(s, "ymlink")) return K_symlink; + if (!strcmp(s, "ysclktz")) return K_sysclktz; + break; + case 't': + if (!strcmp(s, "rigger")) return K_trigger; + break; + case 'u': + if (!strcmp(s, "ser")) return K_user; + break; + case 'w': + if (!strcmp(s, "rite")) return K_write; + if (!strcmp(s, "ait")) return K_wait; + break; + } + return K_UNKNOWN; +} + +void parse_line_no_op(struct parse_state *state, int nargs, char **args) +{ +} + +void parse_new_section(struct parse_state *state, int kw, + int nargs, char **args) +{ + printf("[ %s %s ]\n", args[0], + nargs > 1 ? args[1] : ""); + switch(kw) { + case K_service: + state->context = parse_service(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_service; + return; + } + break; + case K_on: + state->context = parse_action(state, nargs, args); + if (state->context) { + state->parse_line = parse_line_action; + return; + } + break; + } + state->parse_line = parse_line_no_op; +} + +static void parse_config(const char *fn, char *s) +{ + struct parse_state state; + char *args[INIT_PARSER_MAXARGS]; + int nargs; + + nargs = 0; + state.filename = fn; + state.line = 1; + state.ptr = s; + state.nexttoken = 0; + state.parse_line = parse_line_no_op; + for (;;) { + switch (next_token(&state)) { + case T_EOF: + state.parse_line(&state, 0, 0); + return; + case T_NEWLINE: + if (nargs) { + int kw = lookup_keyword(args[0]); + if (kw_is(kw, SECTION)) { + state.parse_line(&state, 0, 0); + parse_new_section(&state, kw, nargs, args); + } else { + state.parse_line(&state, nargs, args); + } + nargs = 0; + } + break; + case T_TEXT: + if (nargs < INIT_PARSER_MAXARGS) { + args[nargs++] = state.text; + } + break; + } + } +} + +int init_parse_config_file(const char *fn) +{ + char *data; + data = read_file(fn, 0); + if (!data) return -1; + + parse_config(fn, data); + DUMP(); + return 0; +} + +static int valid_name(const char *name) +{ + if (strlen(name) > 16) { + return 0; + } + while (*name) { + if (!isalnum(*name) && (*name != '_') && (*name != '-')) { + return 0; + } + name++; + } + return 1; +} + +struct service *service_find_by_name(const char *name) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (!strcmp(svc->name, name)) { + return svc; + } + } + return 0; +} + +struct service *service_find_by_pid(pid_t pid) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->pid == pid) { + return svc; + } + } + return 0; +} + +struct service *service_find_by_keychord(int keychord_id) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->keychord_id == keychord_id) { + return svc; + } + } + return 0; +} + +void service_for_each(void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + func(svc); + } +} + +void service_for_each_class(const char *classname, + void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (!strcmp(svc->classname, classname)) { + func(svc); + } + } +} + +void service_for_each_flags(unsigned matchflags, + void (*func)(struct service *svc)) +{ + struct listnode *node; + struct service *svc; + list_for_each(node, &service_list) { + svc = node_to_item(node, struct service, slist); + if (svc->flags & matchflags) { + func(svc); + } + } +} + +void action_for_each_trigger(const char *trigger, + void (*func)(struct action *act)) +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strcmp(act->name, trigger)) { + func(act); + } + } +} + +void queue_property_triggers(const char *name, const char *value) +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strncmp(act->name, "property:", strlen("property:"))) { + const char *test = act->name + strlen("property:"); + int name_length = strlen(name); + + if (!strncmp(name, test, name_length) && + test[name_length] == '=' && + !strcmp(test + name_length + 1, value)) { + action_add_queue_tail(act); + } + } + } +} + +void queue_all_property_triggers() +{ + struct listnode *node; + struct action *act; + list_for_each(node, &action_list) { + act = node_to_item(node, struct action, alist); + if (!strncmp(act->name, "property:", strlen("property:"))) { + /* parse property name and value + syntax is property:<name>=<value> */ + const char* name = act->name + strlen("property:"); + const char* equals = strchr(name, '='); + if (equals) { + char prop_name[PROP_NAME_MAX + 1]; + const char* value; + int length = equals - name; + if (length > PROP_NAME_MAX) { + ERROR("property name too long in trigger %s", act->name); + } else { + memcpy(prop_name, name, length); + prop_name[length] = 0; + + /* does the property exist, and match the trigger value? */ + value = property_get(prop_name); + if (value && !strcmp(equals + 1, value)) { + action_add_queue_tail(act); + } + } + } + } + } +} + +void queue_builtin_action(int (*func)(int nargs, char **args), char *name) +{ + struct action *act; + struct command *cmd; + + act = calloc(1, sizeof(*act)); + act->name = name; + list_init(&act->commands); + + cmd = calloc(1, sizeof(*cmd)); + cmd->func = func; + cmd->args[0] = name; + list_add_tail(&act->commands, &cmd->clist); + + list_add_tail(&action_list, &act->alist); + action_add_queue_tail(act); +} + +void action_add_queue_tail(struct action *act) +{ + list_add_tail(&action_queue, &act->qlist); +} + +struct action *action_remove_queue_head(void) +{ + if (list_empty(&action_queue)) { + return 0; + } else { + struct listnode *node = list_head(&action_queue); + struct action *act = node_to_item(node, struct action, qlist); + list_remove(node); + return act; + } +} + +int action_queue_empty() +{ + return list_empty(&action_queue); +} + +static void *parse_service(struct parse_state *state, int nargs, char **args) +{ + struct service *svc; + if (nargs < 3) { + parse_error(state, "services must have a name and a program\n"); + return 0; + } + if (!valid_name(args[1])) { + parse_error(state, "invalid service name '%s'\n", args[1]); + return 0; + } + + svc = service_find_by_name(args[1]); + if (svc) { + parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); + return 0; + } + + nargs -= 2; + svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); + if (!svc) { + parse_error(state, "out of memory\n"); + return 0; + } + svc->name = args[1]; + svc->classname = "default"; + memcpy(svc->args, args + 2, sizeof(char*) * nargs); + svc->args[nargs] = 0; + svc->nargs = nargs; + svc->onrestart.name = "onrestart"; + list_init(&svc->onrestart.commands); + list_add_tail(&service_list, &svc->slist); + return svc; +} + +static void parse_line_service(struct parse_state *state, int nargs, char **args) +{ + struct service *svc = state->context; + struct command *cmd; + int i, kw, kw_nargs; + + if (nargs == 0) { + return; + } + + svc->ioprio_class = IoSchedClass_NONE; + + kw = lookup_keyword(args[0]); + switch (kw) { + case K_capability: + break; + case K_class: + if (nargs != 2) { + parse_error(state, "class option requires a classname\n"); + } else { + svc->classname = args[1]; + } + break; + case K_console: + svc->flags |= SVC_CONSOLE; + break; + case K_disabled: + svc->flags |= SVC_DISABLED; + break; + case K_ioprio: + if (nargs != 3) { + parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n"); + } else { + svc->ioprio_pri = strtoul(args[2], 0, 8); + + if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) { + parse_error(state, "priority value must be range 0 - 7\n"); + break; + } + + if (!strcmp(args[1], "rt")) { + svc->ioprio_class = IoSchedClass_RT; + } else if (!strcmp(args[1], "be")) { + svc->ioprio_class = IoSchedClass_BE; + } else if (!strcmp(args[1], "idle")) { + svc->ioprio_class = IoSchedClass_IDLE; + } else { + parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n"); + } + } + break; + case K_group: + if (nargs < 2) { + parse_error(state, "group option requires a group id\n"); + } else if (nargs > NR_SVC_SUPP_GIDS + 2) { + parse_error(state, "group option accepts at most %d supp. groups\n", + NR_SVC_SUPP_GIDS); + } else { + int n; + svc->gid = decode_uid(args[1]); + for (n = 2; n < nargs; n++) { + svc->supp_gids[n-2] = decode_uid(args[n]); + } + svc->nr_supp_gids = n - 2; + } + break; + case K_keycodes: + if (nargs < 2) { + parse_error(state, "keycodes option requires atleast one keycode\n"); + } else { + svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0])); + if (!svc->keycodes) { + parse_error(state, "could not allocate keycodes\n"); + } else { + svc->nkeycodes = nargs - 1; + for (i = 1; i < nargs; i++) { + svc->keycodes[i - 1] = atoi(args[i]); + } + } + } + break; + case K_oneshot: + svc->flags |= SVC_ONESHOT; + break; + case K_onrestart: + nargs--; + args++; + kw = lookup_keyword(args[0]); + if (!kw_is(kw, COMMAND)) { + parse_error(state, "invalid command '%s'\n", args[0]); + break; + } + kw_nargs = kw_nargs(kw); + if (nargs < kw_nargs) { + parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, + kw_nargs > 2 ? "arguments" : "argument"); + break; + } + + cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); + cmd->func = kw_func(kw); + cmd->nargs = nargs; + memcpy(cmd->args, args, sizeof(char*) * nargs); + list_add_tail(&svc->onrestart.commands, &cmd->clist); + break; + case K_critical: + svc->flags |= SVC_CRITICAL; + break; + case K_setenv: { /* name value */ + struct svcenvinfo *ei; + if (nargs < 2) { + parse_error(state, "setenv option requires name and value arguments\n"); + break; + } + ei = calloc(1, sizeof(*ei)); + if (!ei) { + parse_error(state, "out of memory\n"); + break; + } + ei->name = args[1]; + ei->value = args[2]; + ei->next = svc->envvars; + svc->envvars = ei; + break; + } + case K_socket: {/* name type perm [ uid gid ] */ + struct socketinfo *si; + if (nargs < 4) { + parse_error(state, "socket option requires name, type, perm arguments\n"); + break; + } + if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) { + parse_error(state, "socket type must be 'dgram' or 'stream'\n"); + break; + } + si = calloc(1, sizeof(*si)); + if (!si) { + parse_error(state, "out of memory\n"); + break; + } + si->name = args[1]; + si->type = args[2]; + si->perm = strtoul(args[3], 0, 8); + if (nargs > 4) + si->uid = decode_uid(args[4]); + if (nargs > 5) + si->gid = decode_uid(args[5]); + si->next = svc->sockets; + svc->sockets = si; + break; + } + case K_user: + if (nargs != 2) { + parse_error(state, "user option requires a user id\n"); + } else { + svc->uid = decode_uid(args[1]); + } + break; + default: + parse_error(state, "invalid option '%s'\n", args[0]); + } +} + +static void *parse_action(struct parse_state *state, int nargs, char **args) +{ + struct action *act; + if (nargs < 2) { + parse_error(state, "actions must have a trigger\n"); + return 0; + } + if (nargs > 2) { + parse_error(state, "actions may not have extra parameters\n"); + return 0; + } + act = calloc(1, sizeof(*act)); + act->name = args[1]; + list_init(&act->commands); + list_add_tail(&action_list, &act->alist); + /* XXX add to hash */ + return act; +} + +static void parse_line_action(struct parse_state* state, int nargs, char **args) +{ + struct command *cmd; + struct action *act = state->context; + int (*func)(int nargs, char **args); + int kw, n; + + if (nargs == 0) { + return; + } + + kw = lookup_keyword(args[0]); + if (!kw_is(kw, COMMAND)) { + parse_error(state, "invalid command '%s'\n", args[0]); + return; + } + + n = kw_nargs(kw); + if (nargs < n) { + parse_error(state, "%s requires %d %s\n", args[0], n - 1, + n > 2 ? "arguments" : "argument"); + return; + } + cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); + cmd->func = kw_func(kw); + cmd->nargs = nargs; + memcpy(cmd->args, args, sizeof(char*) * nargs); + list_add_tail(&act->commands, &cmd->clist); +} diff --git a/init/init_parser.h b/init/init_parser.h new file mode 100644 index 0000000..ff13b04 --- /dev/null +++ b/init/init_parser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 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_INIT_PARSER_H_ +#define _INIT_INIT_PARSER_H_ + +#define INIT_PARSER_MAXARGS 64 + +struct action; + +struct action *action_remove_queue_head(void); +void action_add_queue_tail(struct action *act); +void action_for_each_trigger(const char *trigger, + void (*func)(struct action *act)); +int action_queue_empty(void); +void queue_property_triggers(const char *name, const char *value); +void queue_all_property_triggers(); +void queue_builtin_action(int (*func)(int nargs, char **args), char *name); + +int init_parse_config_file(const char *fn); + +#endif diff --git a/init/keychords.c b/init/keychords.c new file mode 100644 index 0000000..53ab391 --- /dev/null +++ b/init/keychords.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2010 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 <sys/stat.h> +#include <sys/types.h> +#include <linux/keychord.h> + +#include "init.h" +#include "log.h" +#include "property_service.h" + +static struct input_keychord *keychords = 0; +static int keychords_count = 0; +static int keychords_length = 0; +static int keychord_fd = -1; + +void add_service_keycodes(struct service *svc) +{ + struct input_keychord *keychord; + int i, size; + + if (svc->keycodes) { + /* add a new keychord to the list */ + size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]); + keychords = realloc(keychords, keychords_length + size); + if (!keychords) { + ERROR("could not allocate keychords\n"); + keychords_length = 0; + keychords_count = 0; + return; + } + + keychord = (struct input_keychord *)((char *)keychords + keychords_length); + keychord->version = KEYCHORD_VERSION; + keychord->id = keychords_count + 1; + keychord->count = svc->nkeycodes; + svc->keychord_id = keychord->id; + + for (i = 0; i < svc->nkeycodes; i++) { + keychord->keycodes[i] = svc->keycodes[i]; + } + keychords_count++; + keychords_length += size; + } +} + +void keychord_init() +{ + int fd, ret; + + service_for_each(add_service_keycodes); + + /* nothing to do if no services require keychords */ + if (!keychords) + return; + + fd = open("/dev/keychord", O_RDWR); + if (fd < 0) { + ERROR("could not open /dev/keychord\n"); + return; + } + fcntl(fd, F_SETFD, FD_CLOEXEC); + + ret = write(fd, keychords, keychords_length); + if (ret != keychords_length) { + ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno); + close(fd); + fd = -1; + } + + free(keychords); + keychords = 0; + + keychord_fd = fd; +} + +void handle_keychord() +{ + struct service *svc; + const char* debuggable; + const char* adb_enabled; + 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"); + if ((debuggable && !strcmp(debuggable, "1")) || + (adb_enabled && !strcmp(adb_enabled, "running"))) { + ret = read(keychord_fd, &id, sizeof(id)); + if (ret != sizeof(id)) { + ERROR("could not read keychord id\n"); + return; + } + + svc = service_find_by_keychord(id); + if (svc) { + INFO("starting service %s from keychord\n", svc->name); + service_start(svc, NULL); + } else { + ERROR("service for keychord %d not found\n", id); + } + } +} + +int get_keychord_fd() +{ + return keychord_fd; +} diff --git a/init/keychords.h b/init/keychords.h new file mode 100644 index 0000000..070b858 --- /dev/null +++ b/init/keychords.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 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_KEYCHORDS_H_ +#define _INIT_KEYCHORDS_H_ + +struct service; + +void add_service_keycodes(struct service *svc); +void keychord_init(void); +void handle_keychord(void); +int get_keychord_fd(void); + +#endif diff --git a/init/keywords.h b/init/keywords.h index 254c785..25315d8 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -27,7 +27,7 @@ int do_copy(int nargs, char **args); int do_chown(int nargs, char **args); int do_chmod(int nargs, char **args); int do_loglevel(int nargs, char **args); -int do_device(int nargs, char **args); +int do_wait(int nargs, char **args); #define __MAKE_KEYWORD_ENUM__ #define KEYWORD(symbol, flags, nargs, func) K_##symbol, enum { @@ -69,12 +69,12 @@ enum { KEYWORD(symlink, COMMAND, 1, do_symlink) KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) KEYWORD(user, OPTION, 0, 0) + KEYWORD(wait, COMMAND, 1, do_wait) KEYWORD(write, COMMAND, 2, do_write) KEYWORD(copy, COMMAND, 2, do_copy) KEYWORD(chown, COMMAND, 2, do_chown) KEYWORD(chmod, COMMAND, 2, do_chmod) KEYWORD(loglevel, COMMAND, 1, do_loglevel) - KEYWORD(device, COMMAND, 4, do_device) KEYWORD(ioprio, OPTION, 0, 0) #ifdef __MAKE_KEYWORD_ENUM__ KEYWORD_COUNT, diff --git a/init/list.h b/init/list.h new file mode 100644 index 0000000..0a7b28c --- /dev/null +++ b/init/list.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 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_LIST_H_ +#define _INIT_LIST_H_ + +struct listnode +{ + struct listnode *next; + struct listnode *prev; +}; + +#define node_to_item(node, container, member) \ + (container *) (((char*) (node)) - offsetof(container, member)) + +#define list_declare(name) \ + struct listnode name = { \ + .next = &name, \ + .prev = &name, \ + } + +#define list_for_each(node, list) \ + for (node = (list)->next; node != (list); node = node->next) + +#define list_for_each_reverse(node, list) \ + for (node = (list)->prev; node != (list); node = node->prev) + +void list_init(struct listnode *list); +void list_add_tail(struct listnode *list, struct listnode *item); +void list_remove(struct listnode *item); + +#define list_empty(list) ((list) == (list)->next) +#define list_head(list) ((list)->next) +#define list_tail(list) ((list)->prev) + +#endif diff --git a/init/log.h b/init/log.h new file mode 100644 index 0000000..3d93965 --- /dev/null +++ b/init/log.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 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_LOG_H_ +#define _INIT_LOG_H_ + +void log_init(void); +void log_set_level(int level); +void log_close(void); +void log_write(int level, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); + +#define ERROR(x...) log_write(3, "<3>init: " x) +#define NOTICE(x...) log_write(5, "<5>init: " x) +#define INFO(x...) log_write(6, "<6>init: " x) + +#define LOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ +#define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ + +#endif diff --git a/init/logo.c b/init/logo.c index 6a740bf..614224c 100644 --- a/init/logo.c +++ b/init/logo.c @@ -25,7 +25,7 @@ #include <linux/fb.h> #include <linux/kd.h> -#include "init.h" +#include "log.h" #ifdef ANDROID #include <cutils/memory.h> diff --git a/init/parser.c b/init/parser.c index 7da0d19..2f36ac7 100644 --- a/init/parser.c +++ b/init/parser.c @@ -1,23 +1,10 @@ #include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> #include <stdarg.h> #include <string.h> -#include <stddef.h> -#include <ctype.h> -#include "init.h" -#include "property_service.h" - -#include <cutils/iosched_policy.h> - -#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ -#include <sys/_system_properties.h> - -static list_declare(service_list); -static list_declare(action_list); -static list_declare(action_queue); +#include "parser.h" +#include "list.h" +#include "log.h" #define RAW(x...) log_write(6, x) @@ -62,27 +49,6 @@ void DUMP(void) #endif } -#define T_EOF 0 -#define T_TEXT 1 -#define T_NEWLINE 2 - -struct parse_state -{ - char *ptr; - char *text; - int line; - int nexttoken; - void *context; - void (*parse_line)(struct parse_state *state, int nargs, char **args); - const char *filename; -}; - -static void *parse_service(struct parse_state *state, int nargs, char **args); -static void parse_line_service(struct parse_state *state, int nargs, char **args); - -static void *parse_action(struct parse_state *state, int nargs, char **args); -static void parse_line_action(struct parse_state *state, int nargs, char **args); - void parse_error(struct parse_state *state, const char *fmt, ...) { va_list ap; @@ -100,115 +66,6 @@ void parse_error(struct parse_state *state, const char *fmt, ...) ERROR("%s", buf); } -#define SECTION 0x01 -#define COMMAND 0x02 -#define OPTION 0x04 - -#include "keywords.h" - -#define KEYWORD(symbol, flags, nargs, func) \ - [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, - -struct { - const char *name; - int (*func)(int nargs, char **args); - unsigned char nargs; - unsigned char flags; -} keyword_info[KEYWORD_COUNT] = { - [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, -#include "keywords.h" -}; -#undef KEYWORD - -#define kw_is(kw, type) (keyword_info[kw].flags & (type)) -#define kw_name(kw) (keyword_info[kw].name) -#define kw_func(kw) (keyword_info[kw].func) -#define kw_nargs(kw) (keyword_info[kw].nargs) - -int lookup_keyword(const char *s) -{ - switch (*s++) { - case 'c': - if (!strcmp(s, "opy")) return K_copy; - if (!strcmp(s, "apability")) return K_capability; - if (!strcmp(s, "hdir")) return K_chdir; - if (!strcmp(s, "hroot")) return K_chroot; - if (!strcmp(s, "lass")) return K_class; - if (!strcmp(s, "lass_start")) return K_class_start; - if (!strcmp(s, "lass_stop")) return K_class_stop; - if (!strcmp(s, "onsole")) return K_console; - if (!strcmp(s, "hown")) return K_chown; - if (!strcmp(s, "hmod")) return K_chmod; - if (!strcmp(s, "ritical")) return K_critical; - break; - case 'd': - if (!strcmp(s, "isabled")) return K_disabled; - if (!strcmp(s, "omainname")) return K_domainname; - if (!strcmp(s, "evice")) return K_device; - break; - case 'e': - if (!strcmp(s, "xec")) return K_exec; - if (!strcmp(s, "xport")) return K_export; - break; - case 'g': - if (!strcmp(s, "roup")) return K_group; - break; - case 'h': - if (!strcmp(s, "ostname")) return K_hostname; - break; - case 'i': - if (!strcmp(s, "oprio")) return K_ioprio; - if (!strcmp(s, "fup")) return K_ifup; - if (!strcmp(s, "nsmod")) return K_insmod; - if (!strcmp(s, "mport")) return K_import; - break; - case 'k': - if (!strcmp(s, "eycodes")) return K_keycodes; - break; - case 'l': - if (!strcmp(s, "oglevel")) return K_loglevel; - break; - case 'm': - if (!strcmp(s, "kdir")) return K_mkdir; - if (!strcmp(s, "ount")) return K_mount; - break; - case 'o': - if (!strcmp(s, "n")) return K_on; - if (!strcmp(s, "neshot")) return K_oneshot; - if (!strcmp(s, "nrestart")) return K_onrestart; - break; - case 'r': - if (!strcmp(s, "estart")) return K_restart; - break; - case 's': - if (!strcmp(s, "ervice")) return K_service; - if (!strcmp(s, "etenv")) return K_setenv; - if (!strcmp(s, "etkey")) return K_setkey; - if (!strcmp(s, "etprop")) return K_setprop; - if (!strcmp(s, "etrlimit")) return K_setrlimit; - if (!strcmp(s, "ocket")) return K_socket; - if (!strcmp(s, "tart")) return K_start; - if (!strcmp(s, "top")) return K_stop; - if (!strcmp(s, "ymlink")) return K_symlink; - if (!strcmp(s, "ysclktz")) return K_sysclktz; - break; - case 't': - if (!strcmp(s, "rigger")) return K_trigger; - break; - case 'u': - if (!strcmp(s, "ser")) return K_user; - break; - case 'w': - if (!strcmp(s, "rite")) return K_write; - break; - } - return K_UNKNOWN; -} - -void parse_line_no_op(struct parse_state *state, int nargs, char **args) -{ -} - int next_token(struct parse_state *state) { char *x = state->ptr; @@ -322,504 +179,3 @@ textresume: } return T_EOF; } - -void parse_line(int nargs, char **args) -{ - int n; - int id = lookup_keyword(args[0]); - printf("%s(%d)", args[0], id); - for (n = 1; n < nargs; n++) { - printf(" '%s'", args[n]); - } - printf("\n"); -} - -void parse_new_section(struct parse_state *state, int kw, - int nargs, char **args) -{ - printf("[ %s %s ]\n", args[0], - nargs > 1 ? args[1] : ""); - switch(kw) { - case K_service: - state->context = parse_service(state, nargs, args); - if (state->context) { - state->parse_line = parse_line_service; - return; - } - break; - case K_on: - state->context = parse_action(state, nargs, args); - if (state->context) { - state->parse_line = parse_line_action; - return; - } - break; - } - state->parse_line = parse_line_no_op; -} - -static void parse_config(const char *fn, char *s) -{ - struct parse_state state; - char *args[SVC_MAXARGS]; - int nargs; - - nargs = 0; - state.filename = fn; - state.line = 1; - state.ptr = s; - state.nexttoken = 0; - state.parse_line = parse_line_no_op; - for (;;) { - switch (next_token(&state)) { - case T_EOF: - state.parse_line(&state, 0, 0); - return; - case T_NEWLINE: - if (nargs) { - int kw = lookup_keyword(args[0]); - if (kw_is(kw, SECTION)) { - state.parse_line(&state, 0, 0); - parse_new_section(&state, kw, nargs, args); - } else { - state.parse_line(&state, nargs, args); - } - nargs = 0; - } - break; - case T_TEXT: - if (nargs < SVC_MAXARGS) { - args[nargs++] = state.text; - } - break; - } - } -} - -int parse_config_file(const char *fn) -{ - char *data; - data = read_file(fn, 0); - if (!data) return -1; - - parse_config(fn, data); - DUMP(); - return 0; -} - -static int valid_name(const char *name) -{ - if (strlen(name) > 16) { - return 0; - } - while (*name) { - if (!isalnum(*name) && (*name != '_') && (*name != '-')) { - return 0; - } - name++; - } - return 1; -} - -struct service *service_find_by_name(const char *name) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (!strcmp(svc->name, name)) { - return svc; - } - } - return 0; -} - -struct service *service_find_by_pid(pid_t pid) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (svc->pid == pid) { - return svc; - } - } - return 0; -} - -struct service *service_find_by_keychord(int keychord_id) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (svc->keychord_id == keychord_id) { - return svc; - } - } - return 0; -} - -void service_for_each(void (*func)(struct service *svc)) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - func(svc); - } -} - -void service_for_each_class(const char *classname, - void (*func)(struct service *svc)) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (!strcmp(svc->classname, classname)) { - func(svc); - } - } -} - -void service_for_each_flags(unsigned matchflags, - void (*func)(struct service *svc)) -{ - struct listnode *node; - struct service *svc; - list_for_each(node, &service_list) { - svc = node_to_item(node, struct service, slist); - if (svc->flags & matchflags) { - func(svc); - } - } -} - -void action_for_each_trigger(const char *trigger, - void (*func)(struct action *act)) -{ - struct listnode *node; - struct action *act; - list_for_each(node, &action_list) { - act = node_to_item(node, struct action, alist); - if (!strcmp(act->name, trigger)) { - func(act); - } - } -} - -void queue_property_triggers(const char *name, const char *value) -{ - struct listnode *node; - struct action *act; - list_for_each(node, &action_list) { - act = node_to_item(node, struct action, alist); - if (!strncmp(act->name, "property:", strlen("property:"))) { - const char *test = act->name + strlen("property:"); - int name_length = strlen(name); - - if (!strncmp(name, test, name_length) && - test[name_length] == '=' && - !strcmp(test + name_length + 1, value)) { - action_add_queue_tail(act); - } - } - } -} - -void queue_all_property_triggers() -{ - struct listnode *node; - struct action *act; - list_for_each(node, &action_list) { - act = node_to_item(node, struct action, alist); - if (!strncmp(act->name, "property:", strlen("property:"))) { - /* parse property name and value - syntax is property:<name>=<value> */ - const char* name = act->name + strlen("property:"); - const char* equals = strchr(name, '='); - if (equals) { - char prop_name[PROP_NAME_MAX + 1]; - const char* value; - int length = equals - name; - if (length > PROP_NAME_MAX) { - ERROR("property name too long in trigger %s", act->name); - } else { - memcpy(prop_name, name, length); - prop_name[length] = 0; - - /* does the property exist, and match the trigger value? */ - value = property_get(prop_name); - if (value && !strcmp(equals + 1, value)) { - action_add_queue_tail(act); - } - } - } - } - } -} - -void action_add_queue_tail(struct action *act) -{ - list_add_tail(&action_queue, &act->qlist); -} - -struct action *action_remove_queue_head(void) -{ - if (list_empty(&action_queue)) { - return 0; - } else { - struct listnode *node = list_head(&action_queue); - struct action *act = node_to_item(node, struct action, qlist); - list_remove(node); - return act; - } -} - -static void *parse_service(struct parse_state *state, int nargs, char **args) -{ - struct service *svc; - if (nargs < 3) { - parse_error(state, "services must have a name and a program\n"); - return 0; - } - if (!valid_name(args[1])) { - parse_error(state, "invalid service name '%s'\n", args[1]); - return 0; - } - - svc = service_find_by_name(args[1]); - if (svc) { - parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); - return 0; - } - - nargs -= 2; - svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs); - if (!svc) { - parse_error(state, "out of memory\n"); - return 0; - } - svc->name = args[1]; - svc->classname = "default"; - memcpy(svc->args, args + 2, sizeof(char*) * nargs); - svc->args[nargs] = 0; - svc->nargs = nargs; - svc->onrestart.name = "onrestart"; - list_init(&svc->onrestart.commands); - list_add_tail(&service_list, &svc->slist); - return svc; -} - -static void parse_line_service(struct parse_state *state, int nargs, char **args) -{ - struct service *svc = state->context; - struct command *cmd; - int i, kw, kw_nargs; - - if (nargs == 0) { - return; - } - - svc->ioprio_class = IoSchedClass_NONE; - - kw = lookup_keyword(args[0]); - switch (kw) { - case K_capability: - break; - case K_class: - if (nargs != 2) { - parse_error(state, "class option requires a classname\n"); - } else { - svc->classname = args[1]; - } - break; - case K_console: - svc->flags |= SVC_CONSOLE; - break; - case K_disabled: - svc->flags |= SVC_DISABLED; - break; - case K_ioprio: - if (nargs != 3) { - parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n"); - } else { - svc->ioprio_pri = strtoul(args[2], 0, 8); - - if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) { - parse_error(state, "priority value must be range 0 - 7\n"); - break; - } - - if (!strcmp(args[1], "rt")) { - svc->ioprio_class = IoSchedClass_RT; - } else if (!strcmp(args[1], "be")) { - svc->ioprio_class = IoSchedClass_BE; - } else if (!strcmp(args[1], "idle")) { - svc->ioprio_class = IoSchedClass_IDLE; - } else { - parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n"); - } - } - break; - case K_group: - if (nargs < 2) { - parse_error(state, "group option requires a group id\n"); - } else if (nargs > NR_SVC_SUPP_GIDS + 2) { - parse_error(state, "group option accepts at most %d supp. groups\n", - NR_SVC_SUPP_GIDS); - } else { - int n; - svc->gid = decode_uid(args[1]); - for (n = 2; n < nargs; n++) { - svc->supp_gids[n-2] = decode_uid(args[n]); - } - svc->nr_supp_gids = n - 2; - } - break; - case K_keycodes: - if (nargs < 2) { - parse_error(state, "keycodes option requires atleast one keycode\n"); - } else { - svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0])); - if (!svc->keycodes) { - parse_error(state, "could not allocate keycodes\n"); - } else { - svc->nkeycodes = nargs - 1; - for (i = 1; i < nargs; i++) { - svc->keycodes[i - 1] = atoi(args[i]); - } - } - } - break; - case K_oneshot: - svc->flags |= SVC_ONESHOT; - break; - case K_onrestart: - nargs--; - args++; - kw = lookup_keyword(args[0]); - if (!kw_is(kw, COMMAND)) { - parse_error(state, "invalid command '%s'\n", args[0]); - break; - } - kw_nargs = kw_nargs(kw); - if (nargs < kw_nargs) { - parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1, - kw_nargs > 2 ? "arguments" : "argument"); - break; - } - - cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); - cmd->func = kw_func(kw); - cmd->nargs = nargs; - memcpy(cmd->args, args, sizeof(char*) * nargs); - list_add_tail(&svc->onrestart.commands, &cmd->clist); - break; - case K_critical: - svc->flags |= SVC_CRITICAL; - break; - case K_setenv: { /* name value */ - struct svcenvinfo *ei; - if (nargs < 2) { - parse_error(state, "setenv option requires name and value arguments\n"); - break; - } - ei = calloc(1, sizeof(*ei)); - if (!ei) { - parse_error(state, "out of memory\n"); - break; - } - ei->name = args[1]; - ei->value = args[2]; - ei->next = svc->envvars; - svc->envvars = ei; - break; - } - case K_socket: {/* name type perm [ uid gid ] */ - struct socketinfo *si; - if (nargs < 4) { - parse_error(state, "socket option requires name, type, perm arguments\n"); - break; - } - if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) { - parse_error(state, "socket type must be 'dgram' or 'stream'\n"); - break; - } - si = calloc(1, sizeof(*si)); - if (!si) { - parse_error(state, "out of memory\n"); - break; - } - si->name = args[1]; - si->type = args[2]; - si->perm = strtoul(args[3], 0, 8); - if (nargs > 4) - si->uid = decode_uid(args[4]); - if (nargs > 5) - si->gid = decode_uid(args[5]); - si->next = svc->sockets; - svc->sockets = si; - break; - } - case K_user: - if (nargs != 2) { - parse_error(state, "user option requires a user id\n"); - } else { - svc->uid = decode_uid(args[1]); - } - break; - default: - parse_error(state, "invalid option '%s'\n", args[0]); - } -} - -static void *parse_action(struct parse_state *state, int nargs, char **args) -{ - struct action *act; - if (nargs < 2) { - parse_error(state, "actions must have a trigger\n"); - return 0; - } - if (nargs > 2) { - parse_error(state, "actions may not have extra parameters\n"); - return 0; - } - act = calloc(1, sizeof(*act)); - act->name = args[1]; - list_init(&act->commands); - list_add_tail(&action_list, &act->alist); - /* XXX add to hash */ - return act; -} - -static void parse_line_action(struct parse_state* state, int nargs, char **args) -{ - struct command *cmd; - struct action *act = state->context; - int (*func)(int nargs, char **args); - int kw, n; - - if (nargs == 0) { - return; - } - - kw = lookup_keyword(args[0]); - if (!kw_is(kw, COMMAND)) { - parse_error(state, "invalid command '%s'\n", args[0]); - return; - } - - n = kw_nargs(kw); - if (nargs < n) { - parse_error(state, "%s requires %d %s\n", args[0], n - 1, - n > 2 ? "arguments" : "argument"); - return; - } - cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs); - cmd->func = kw_func(kw); - cmd->nargs = nargs; - memcpy(cmd->args, args, sizeof(char*) * nargs); - list_add_tail(&act->commands, &cmd->clist); -} diff --git a/init/parser.h b/init/parser.h new file mode 100644 index 0000000..be93758 --- /dev/null +++ b/init/parser.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 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 PARSER_H_ +#define PARSER_H_ + +#define T_EOF 0 +#define T_TEXT 1 +#define T_NEWLINE 2 + +struct parse_state +{ + char *ptr; + char *text; + int line; + int nexttoken; + void *context; + void (*parse_line)(struct parse_state *state, int nargs, char **args); + const char *filename; +}; + +int lookup_keyword(const char *s); +void DUMP(void); +int next_token(struct parse_state *state); +void parse_error(struct parse_state *state, const char *fmt, ...); + +#endif /* PARSER_H_ */ diff --git a/init/property_service.c b/init/property_service.c index d2505dd..e35cd38 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -43,10 +43,15 @@ #include "property_service.h" #include "init.h" +#include "util.h" +#include "log.h" #define PERSISTENT_PROPERTY_DIR "/data/property" static int persistent_properties_loaded = 0; +static int property_area_inited = 0; + +static int property_set_fd = -1; /* White list of permissions for setting property services. */ struct { @@ -160,7 +165,7 @@ static int init_property_area(void) /* plug into the lib property services */ __system_property_area__ = pa; - + property_area_inited = 1; return 0; } @@ -187,7 +192,7 @@ static int property_write(prop_info *pi, const char *value) * * Returns 1 if uid allowed, 0 otherwise. */ -static int check_control_perms(const char *name, int uid, int gid) { +static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) { int i; if (uid == AID_SYSTEM || uid == AID_ROOT) return 1; @@ -208,7 +213,7 @@ static int check_control_perms(const char *name, int uid, int gid) { * Checks permissions for setting system properties. * Returns 1 if uid allowed, 0 otherwise. */ -static int check_perms(const char *name, unsigned int uid, int gid) +static int check_perms(const char *name, unsigned int uid, unsigned int gid) { int i; if (uid == 0) @@ -344,7 +349,7 @@ static int property_list(void (*propfn)(const char *key, const char *value, void return 0; } -void handle_property_set_fd(int fd) +void handle_property_set_fd() { prop_msg msg; int s; @@ -355,7 +360,7 @@ void handle_property_set_fd(int fd) socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); - if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) { + if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } @@ -493,7 +498,12 @@ void property_init(void) load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); } -int start_property_service(void) +int properties_inited(void) +{ + return property_area_inited; +} + +void start_property_service(void) { int fd; @@ -504,10 +514,15 @@ int start_property_service(void) load_persistent_properties(); fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); - if(fd < 0) return -1; + if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); - return fd; + property_set_fd = fd; +} + +int get_property_set_fd() +{ + return property_set_fd; } diff --git a/init/property_service.h b/init/property_service.h index d12f1f3..045d20a 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -17,12 +17,13 @@ #ifndef _INIT_PROPERTY_H #define _INIT_PROPERTY_H -extern void handle_property_fd(int fd); -extern void handle_property_set_fd(int fd); +extern void handle_property_set_fd(void); extern void property_init(void); -extern int start_property_service(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_set(const char *name, const char *value); +extern int properties_inited(); +int get_property_set_fd(void); #endif /* _INIT_PROPERTY_H */ diff --git a/init/signal_handler.c b/init/signal_handler.c new file mode 100644 index 0000000..3e5d136 --- /dev/null +++ b/init/signal_handler.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2010 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 <stdio.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <cutils/sockets.h> +#include <sys/reboot.h> + +#include "init.h" +#include "list.h" +#include "util.h" +#include "log.h" + +static int signal_fd = -1; +static int signal_recv_fd = -1; + +static void sigchld_handler(int s) +{ + write(signal_fd, &s, 1); +} + +#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */ +#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/ + +static int wait_for_one_process(int block) +{ + pid_t pid; + int status; + struct service *svc; + struct socketinfo *si; + time_t now; + struct listnode *node; + struct command *cmd; + + while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); + if (pid <= 0) return -1; + INFO("waitpid returned pid %d, status = %08x\n", pid, status); + + svc = service_find_by_pid(pid); + if (!svc) { + ERROR("untracked pid %d exited\n", pid); + return 0; + } + + NOTICE("process '%s', pid %d exited\n", svc->name, pid); + + if (!(svc->flags & SVC_ONESHOT)) { + kill(-pid, SIGKILL); + NOTICE("process '%s' killing any children in process group\n", svc->name); + } + + /* remove any sockets we may have created */ + for (si = svc->sockets; si; si = si->next) { + char tmp[128]; + snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); + unlink(tmp); + } + + svc->pid = 0; + svc->flags &= (~SVC_RUNNING); + + /* oneshot processes go into the disabled state on exit */ + if (svc->flags & SVC_ONESHOT) { + svc->flags |= SVC_DISABLED; + } + + /* disabled processes do not get restarted automatically */ + if (svc->flags & SVC_DISABLED) { + notify_service_state(svc->name, "stopped"); + return 0; + } + + now = gettime(); + if (svc->flags & SVC_CRITICAL) { + if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { + if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { + ERROR("critical process '%s' exited %d times in %d minutes; " + "rebooting into recovery mode\n", svc->name, + CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); + sync(); + __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART2, "recovery"); + return 0; + } + } else { + svc->time_crashed = now; + svc->nr_crashed = 1; + } + } + + svc->flags |= SVC_RESTARTING; + + /* Execute all onrestart commands for this service. */ + list_for_each(node, &svc->onrestart.commands) { + cmd = node_to_item(node, struct command, clist); + cmd->func(cmd->nargs, cmd->args); + } + notify_service_state(svc->name, "restarting"); + return 0; +} + +void handle_signal(void) +{ + char tmp[32]; + + /* we got a SIGCHLD - reap and restart as needed */ + read(signal_recv_fd, tmp, sizeof(tmp)); + while (!wait_for_one_process(0)) + ; +} + +void signal_init(void) +{ + int s[2]; + + struct sigaction 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 */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { + signal_fd = s[0]; + signal_recv_fd = s[1]; + fcntl(s[0], F_SETFD, FD_CLOEXEC); + fcntl(s[0], F_SETFL, O_NONBLOCK); + fcntl(s[1], F_SETFD, FD_CLOEXEC); + fcntl(s[1], F_SETFL, O_NONBLOCK); + } + + handle_signal(); +} + +int get_signal_fd() +{ + return signal_recv_fd; +} diff --git a/init/signal_handler.h b/init/signal_handler.h new file mode 100644 index 0000000..b092ccb --- /dev/null +++ b/init/signal_handler.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2010 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_SIGNAL_HANDLER_H_ +#define _INIT_SIGNAL_HANDLER_H_ + +void signal_init(void); +void handle_signal(void); +int get_signal_fd(void); + +#endif diff --git a/init/ueventd.c b/init/ueventd.c new file mode 100644 index 0000000..d51ffde --- /dev/null +++ b/init/ueventd.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010 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 <poll.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <private/android_filesystem_config.h> + +#include "ueventd.h" +#include "log.h" +#include "util.h" +#include "devices.h" +#include "ueventd_parser.h" + +static char hardware[32]; +static unsigned revision = 0; + +int ueventd_main(int argc, char **argv) +{ + struct pollfd ufd; + int nr; + char tmp[32]; + + open_devnull_stdio(); + log_init(); + + INFO("starting ueventd\n"); + + get_hardware_name(hardware, &revision); + + ueventd_parse_config_file("/ueventd.rc"); + + snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware); + ueventd_parse_config_file(tmp); + + device_init(); + + ufd.events = POLLIN; + ufd.fd = get_device_fd(); + + while(1) { + ufd.revents = 0; + nr = poll(&ufd, 1, -1); + if (nr <= 0) + continue; + if (ufd.revents == POLLIN) + handle_device_fd(); + } +} + +static int get_android_id(const char *id) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(android_ids); i++) + if (!strcmp(id, android_ids[i].name)) + return android_ids[i].aid; + return 0; +} + +void set_device_permission(int nargs, char **args) +{ + char *name; + mode_t perm; + uid_t uid; + gid_t gid; + int prefix = 0; + char *endptr; + int ret; + char *tmp = 0; + + if (nargs == 0) + return; + + if (args[0][0] == '#') + return; + + if (nargs != 4) { + ERROR("invalid line ueventd.rc line for '%s'\n", args[0]); + return; + } + + name = args[0]; + /* If path starts with mtd@ lookup the mount number. */ + if (!strncmp(name, "mtd@", 4)) { + int n = mtd_name_to_number(name + 4); + if (n >= 0) + asprintf(&tmp, "/dev/mtd/mtd%d", n); + name = tmp; + } else { + int len = strlen(name); + if (name[len - 1] == '*') { + prefix = 1; + name[len - 1] = '\0'; + } + } + + perm = strtol(args[1], &endptr, 8); + if (!endptr || *endptr != '\0') { + ERROR("invalid mode '%s'\n", args[1]); + free(tmp); + return; + } + + ret = get_android_id(args[2]); + if (ret < 0) { + ERROR("invalid uid '%s'\n", args[2]); + free(tmp); + return; + } + uid = ret; + + ret = get_android_id(args[3]); + if (ret < 0) { + ERROR("invalid gid '%s'\n", args[3]); + free(tmp); + return; + } + gid = ret; + + add_dev_perms(name, perm, uid, gid, prefix); + free(tmp); +} diff --git a/init/ueventd.h b/init/ueventd.h new file mode 100644 index 0000000..9066e47 --- /dev/null +++ b/init/ueventd.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010 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_UEVENTD_H_ +#define _INIT_UEVENTD_H_ + +int ueventd_main(int argc, char **argv); + +#endif diff --git a/init/ueventd_parser.c b/init/ueventd_parser.c new file mode 100644 index 0000000..0dd8b4d --- /dev/null +++ b/init/ueventd_parser.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 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 <stdio.h> +#include <unistd.h> +#include <stdarg.h> +#include <string.h> + +#include "ueventd_parser.h" +#include "parser.h" +#include "log.h" +#include "list.h" +#include "util.h" + +static void parse_line_device(struct parse_state *state, int nargs, char **args); + +static void parse_config(const char *fn, char *s) +{ + struct parse_state state; + char *args[UEVENTD_PARSER_MAXARGS]; + int nargs; + nargs = 0; + state.filename = fn; + state.line = 1; + state.ptr = s; + state.nexttoken = 0; + state.parse_line = parse_line_device; + for (;;) { + int token = next_token(&state); + switch (token) { + case T_EOF: + state.parse_line(&state, 0, 0); + return; + case T_NEWLINE: + if (nargs) { + state.parse_line(&state, nargs, args); + nargs = 0; + } + break; + case T_TEXT: + if (nargs < UEVENTD_PARSER_MAXARGS) { + args[nargs++] = state.text; + } + break; + } + } +} + +int ueventd_parse_config_file(const char *fn) +{ + char *data; + data = read_file(fn, 0); + if (!data) return -1; + + parse_config(fn, data); + DUMP(); + return 0; +} + +static void parse_line_device(struct parse_state* state, int nargs, char **args) +{ + set_device_permission(nargs, args); +} diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h new file mode 100644 index 0000000..48f9bb8 --- /dev/null +++ b/init/ueventd_parser.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 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_UEVENTD_PARSER_H_ +#define _INIT_UEVENTD_PARSER_H_ + +#define UEVENTD_PARSER_MAXARGS 4 + +int ueventd_parse_config_file(const char *fn); +void set_device_permission(int nargs, char **args); + +#endif diff --git a/init/util.c b/init/util.c index 0b7667d..377754b 100644 --- a/init/util.c +++ b/init/util.c @@ -21,6 +21,7 @@ #include <fcntl.h> #include <ctype.h> #include <errno.h> +#include <time.h> #include <sys/stat.h> #include <sys/types.h> @@ -32,7 +33,9 @@ #include <private/android_filesystem_config.h> -#include "init.h" +#include "log.h" +#include "list.h" +#include "util.h" static int log_fd = -1; /* Inital log level before init.rc is parsed and this this is reset. */ @@ -209,3 +212,246 @@ void list_remove(struct listnode *item) item->prev->next = item->next; } +#define MAX_MTD_PARTITIONS 16 + +static struct { + char name[16]; + int number; +} mtd_part_map[MAX_MTD_PARTITIONS]; + +static int mtd_part_count = -1; + +static void find_mtd_partitions(void) +{ + int fd; + char buf[1024]; + char *pmtdbufp; + ssize_t pmtdsize; + int r; + + fd = open("/proc/mtd", O_RDONLY); + if (fd < 0) + return; + + buf[sizeof(buf) - 1] = '\0'; + pmtdsize = read(fd, buf, sizeof(buf) - 1); + pmtdbufp = buf; + while (pmtdsize > 0) { + int mtdnum, mtdsize, mtderasesize; + char mtdname[16]; + mtdname[0] = '\0'; + mtdnum = -1; + r = sscanf(pmtdbufp, "mtd%d: %x %x %15s", + &mtdnum, &mtdsize, &mtderasesize, mtdname); + if ((r == 4) && (mtdname[0] == '"')) { + char *x = strchr(mtdname + 1, '"'); + if (x) { + *x = 0; + } + INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1); + if (mtd_part_count < MAX_MTD_PARTITIONS) { + strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1); + mtd_part_map[mtd_part_count].number = mtdnum; + mtd_part_count++; + } else { + ERROR("too many mtd partitions\n"); + } + } + while (pmtdsize > 0 && *pmtdbufp != '\n') { + pmtdbufp++; + pmtdsize--; + } + if (pmtdsize > 0) { + pmtdbufp++; + pmtdsize--; + } + } + close(fd); +} + +int mtd_name_to_number(const char *name) +{ + int n; + if (mtd_part_count < 0) { + mtd_part_count = 0; + find_mtd_partitions(); + } + for (n = 0; n < mtd_part_count; n++) { + if (!strcmp(name, mtd_part_map[n].name)) { + return mtd_part_map[n].number; + } + } + return -1; +} + +/* + * gettime() - returns the time in seconds of the system's monotonic clock or + * zero on error. + */ +time_t gettime(void) +{ + struct timespec ts; + int ret; + + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret < 0) { + ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); + return 0; + } + + return ts.tv_sec; +} + +int mkdir_recursive(const char *pathname, mode_t mode) +{ + char buf[128]; + const char *slash; + const char *p = pathname; + int width; + int ret; + struct stat info; + + while ((slash = strchr(p, '/')) != NULL) { + width = slash - pathname; + p = slash + 1; + if (width < 0) + break; + if (width == 0) + continue; + if ((unsigned int)width > sizeof(buf) - 1) { + ERROR("path too long for mkdir_recursive\n"); + return -1; + } + memcpy(buf, pathname, width); + buf[width] = 0; + if (stat(buf, &info) != 0) { + ret = mkdir(buf, mode); + if (ret && errno != EEXIST) + return ret; + } + } + ret = mkdir(pathname, mode); + if (ret && errno != EEXIST) + return ret; + return 0; +} + +void sanitize(char *s) +{ + if (!s) + return; + while (isalnum(*s)) + s++; + *s = 0; +} +void make_link(const char *oldpath, const char *newpath) +{ + int ret; + char buf[256]; + char *slash; + int width; + + slash = strrchr(newpath, '/'); + if (!slash) + return; + width = slash - newpath; + if (width <= 0 || width > (int)sizeof(buf) - 1) + return; + memcpy(buf, newpath, width); + buf[width] = 0; + ret = mkdir_recursive(buf, 0755); + if (ret) + ERROR("Failed to create directory %s: %s (%d)\n", buf, strerror(errno), errno); + + ret = symlink(oldpath, newpath); + if (ret && errno != EEXIST) + ERROR("Failed to symlink %s to %s: %s (%d)\n", oldpath, newpath, strerror(errno), errno); +} + +void remove_link(const char *oldpath, const char *newpath) +{ + char path[256]; + ssize_t ret; + ret = readlink(newpath, path, sizeof(path) - 1); + if (ret <= 0) + return; + path[ret] = 0; + if (!strcmp(path, oldpath)) + unlink(newpath); +} + +int wait_for_file(const char *filename, int timeout) +{ + struct stat info; + time_t timeout_time = gettime() + timeout; + int ret = -1; + + while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0)) + usleep(10000); + + return ret; +} + +void open_devnull_stdio(void) +{ + int fd; + static const char *name = "/dev/__null__"; + if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) { + fd = open(name, O_RDWR); + unlink(name); + if (fd >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) { + close(fd); + } + return; + } + } + + exit(1); +} + +void get_hardware_name(char *hardware, unsigned int *revision) +{ + char data[1024]; + int fd, n; + char *x, *hw, *rev; + + /* Hardware string was provided on kernel command line */ + if (hardware[0]) + return; + + fd = open("/proc/cpuinfo", O_RDONLY); + if (fd < 0) return; + + n = read(fd, data, 1023); + close(fd); + if (n < 0) return; + + data[n] = 0; + hw = strstr(data, "\nHardware"); + rev = strstr(data, "\nRevision"); + + if (hw) { + x = strstr(hw, ": "); + if (x) { + x += 2; + n = 0; + while (*x && !isspace(*x)) { + hardware[n++] = tolower(*x); + x++; + if (n == 31) break; + } + hardware[n] = 0; + } + } + + if (rev) { + x = strstr(rev, ": "); + if (x) { + *revision = strtoul(x + 2, 0, 16); + } + } +} diff --git a/init/util.h b/init/util.h new file mode 100644 index 0000000..2e47369 --- /dev/null +++ b/init/util.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 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_UTIL_H_ +#define _INIT_UTIL_H_ + +#include <sys/stat.h> +#include <sys/types.h> + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +static const char *coldboot_done = "/dev/.coldboot_done"; + +int mtd_name_to_number(const char *name); +int create_socket(const char *name, int type, mode_t perm, + uid_t uid, gid_t gid); +void *read_file(const char *fn, unsigned *_sz); +time_t gettime(void); +unsigned int decode_uid(const char *s); + +int mkdir_recursive(const char *pathname, mode_t mode); +void sanitize(char *p); +void make_link(const char *oldpath, const char *newpath); +void remove_link(const char *oldpath, const char *newpath); +int wait_for_file(const char *filename, int timeout); +void open_devnull_stdio(void); +void get_hardware_name(char *hardware, unsigned int *revision); +#endif diff --git a/libacc/acc.cpp b/libacc/acc.cpp index 8f33b0b..6e13f9a 100644 --- a/libacc/acc.cpp +++ b/libacc/acc.cpp @@ -23,10 +23,11 @@ #include <cutils/hashmap.h> -#if defined(__i386__) #include <sys/mman.h> -#endif +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +#define MAP_ANONYMOUS MAP_ANON +#endif #if defined(__arm__) #define DEFAULT_ARM_CODEGEN @@ -230,7 +231,7 @@ class Compiler : public ErrorSink { void release() { if (pProgramBase != 0) { - free(pProgramBase); + munmap(pProgramBase, mSize); pProgramBase = 0; } } @@ -263,7 +264,9 @@ class Compiler : public ErrorSink { virtual void init(int size) { release(); mSize = size; - pProgramBase = (char*) calloc(1, size); + pProgramBase = (char*) mmap(NULL, size, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ind = pProgramBase; } diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 4c45cc9..5b05a1e 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -16,6 +16,13 @@ LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) +ifeq ($(TARGET_CPU_SMP),true) + targetSmpFlag := -DANDROID_SMP=1 +else + targetSmpFlag := -DANDROID_SMP=0 +endif +hostSmpFlag := -DANDROID_SMP=0 + commonSources := \ array.c \ hashmap.c \ @@ -80,6 +87,7 @@ LOCAL_MODULE := libcutils LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := liblog +LOCAL_CFLAGS += $(hostSmpFlag) include $(BUILD_HOST_STATIC_LIBRARY) @@ -92,6 +100,7 @@ LOCAL_MODULE := libcutils LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) memory.c dlmalloc_stubs.c LOCAL_LDLIBS := -lpthread LOCAL_SHARED_LIBRARIES := liblog +LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_SHARED_LIBRARY) else #!sim @@ -114,12 +123,14 @@ endif # !arm LOCAL_C_INCLUDES := $(KERNEL_HEADERS) LOCAL_STATIC_LIBRARIES := liblog +LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libcutils LOCAL_WHOLE_STATIC_LIBRARIES := libcutils LOCAL_SHARED_LIBRARIES := liblog +LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_SHARED_LIBRARY) endif #!sim diff --git a/libcutils/atomic-android-arm.S b/libcutils/atomic-android-arm.S index 1dd2363..d8ee15c 100644 --- a/libcutils/atomic-android-arm.S +++ b/libcutils/atomic-android-arm.S @@ -16,62 +16,351 @@ #include <machine/cpu-features.h> + .text + .align + + .global android_atomic_acquire_load + .type android_atomic_acquire_load, %function + .global android_atomic_release_load + .type android_atomic_release_load, %function + + .global android_atomic_acquire_store + .type android_atomic_acquire_store, %function + .global android_atomic_release_store + .type android_atomic_release_store, %function + + .global android_atomic_inc + .type android_atomic_inc, %function + .global android_atomic_dec + .type android_atomic_dec, %function + + .global android_atomic_add + .type android_atomic_add, %function + .global android_atomic_and + .type android_atomic_and, %function + .global android_atomic_or + .type android_atomic_or, %function + + .global android_atomic_release_swap + .type android_atomic_release_swap, %function + .global android_atomic_acquire_swap + .type android_atomic_acquire_swap, %function + + .global android_atomic_release_cas + .type android_atomic_release_cas, %function + .global android_atomic_acquire_cas + .type android_atomic_acquire_cas, %function + +/* must be on or off; cannot be left undefined */ +#if !defined(ANDROID_SMP) +# error "ANDROID_SMP not defined" +#endif + + +#if defined(__ARM_HAVE_LDREX_STREX) +/* + * =========================================================================== + * ARMv6+ implementation + * =========================================================================== + * + * These functions use the LDREX/STREX instructions to perform atomic + * operations ("LL/SC" approach). On an SMP build they will include + * an appropriate memory barrier. + */ + +/* generate the memory barrier instruction when the build requires it */ +#if ANDROID_SMP == 1 +# if defined(__ARM_HAVE_DMB) +# define SMP_DMB dmb +# else + /* Data Memory Barrier operation, initated by writing a value into a + specific register with the Move to Coprocessor instruction. We + arbitrarily use r0 here. */ +# define SMP_DMB mcr p15, 0, r0, c7, c10, 5 +# endif +#else +# define SMP_DMB +#endif + +/* + * Sidebar: do we need to use the -EX instructions for atomic load/store? + * + * Consider the following situation (time advancing downward): + * + * P1 P2 + * val = LDREX(mem) + * val = val + 1 + * STR(mem, otherval) + * STREX(mem, val) + * + * If these instructions issue on separate cores, the STREX will correctly + * fail because of the intervening store from the other core. If this same + * sequence of instructions executes in two threads on the same core, the + * STREX will incorrectly succeed. + * + * There are two ways to fix this: + * (1) Use LDREX/STREX for the atomic store operations. This doesn't + * prevent the program from doing a non-exclusive store, but at least + * this way if they always use atomic ops to access the memory location + * there won't be any problems. + * (2) Have the kernel clear the LDREX reservation on thread context switch. + * This will sometimes clear the reservation unnecessarily, but guarantees + * correct behavior. + * + * The Android kernel performs a CLREX (v7) or dummy STREX (pre-v7), so we + * can get away with a non-exclusive store here. + * + * ----- + * + * It's worth noting that using non-exclusive LDR and STR means the "load" + * and "store" operations aren't quite the same as read-modify-write or + * swap operations. By definition those must read and write memory in a + * in a way that is coherent across all cores, whereas our non-exclusive + * load and store have no such requirement. + * + * In practice this doesn't matter, because the only guarantees we make + * about who sees what when are tied to the acquire/release semantics. + * Other cores may not see our atomic releasing store as soon as they would + * if the code used LDREX/STREX, but a store-release operation doesn't make + * any guarantees as to how soon the store will be visible. It's allowable + * for operations that happen later in program order to become visible + * before the store. For an acquring store we issue a full barrier after + * the STREX, ensuring that other processors see events in the proper order. + */ + +/* + * android_atomic_acquire_load / android_atomic_release_load + * input: r0 = address + * output: r0 = value + */ +android_atomic_acquire_load: + .fnstart + ldr r0, [r0] + SMP_DMB + bx lr + .fnend + +android_atomic_release_load: + .fnstart + SMP_DMB + ldr r0, [r0] + bx lr + .fnend + + +/* + * android_atomic_acquire_store / android_atomic_release_store + * input: r0 = value, r1 = address + * output: void + */ +android_atomic_acquire_store: + .fnstart + str r0, [r1] + SMP_DMB + bx lr + .fnend + +android_atomic_release_store: + .fnstart + SMP_DMB + str r0, [r1] + bx lr + .fnend + +/* + * Common sequence for read-modify-write operations. + * + * input: r1 = address + * output: r0 = original value, returns to caller + */ + .macro RMWEX op, arg +1: ldrex r0, [r1] @ load current value into r0 + \op r2, r0, \arg @ generate new value into r2 + strex r3, r2, [r1] @ try to store new value; result in r3 + cmp r3, #0 @ success? + bxeq lr @ yes, return + b 1b @ no, retry + .endm + + +/* + * android_atomic_inc + * input: r0 = address + * output: r0 = old value + */ +android_atomic_inc: + .fnstart + SMP_DMB + mov r1, r0 + RMWEX add, #1 + .fnend + + +/* + * android_atomic_dec + * input: r0 = address + * output: r0 = old value + */ +android_atomic_dec: + .fnstart + SMP_DMB + mov r1, r0 + RMWEX sub, #1 + .fnend + + +/* + * android_atomic_add + * input: r0 = value, r1 = address + * output: r0 = old value + */ +android_atomic_add: + .fnstart + SMP_DMB + mov ip, r0 + RMWEX add, ip + .fnend + + +/* + * android_atomic_and + * input: r0 = value, r1 = address + * output: r0 = old value + */ +android_atomic_and: + .fnstart + SMP_DMB + mov ip, r0 + RMWEX and, ip + .fnend + + /* - * NOTE: these atomic operations are SMP safe on all architectures. + * android_atomic_or + * input: r0 = value, r1 = address + * output: r0 = old value */ +android_atomic_or: + .fnstart + SMP_DMB + mov ip, r0 + RMWEX orr, ip + .fnend - .text - .align - - .global android_atomic_write - .type android_atomic_write, %function - - .global android_atomic_inc - .type android_atomic_inc, %function - .global android_atomic_dec - .type android_atomic_dec, %function - - .global android_atomic_add - .type android_atomic_add, %function - .global android_atomic_and - .type android_atomic_and, %function - .global android_atomic_or - .type android_atomic_or, %function - - .global android_atomic_swap - .type android_atomic_swap, %function - - .global android_atomic_cmpxchg - .type android_atomic_cmpxchg, %function /* - * ---------------------------------------------------------------------------- + * android_atomic_acquire_swap / android_atomic_release_swap + * input: r0 = value, r1 = address + * output: r0 = old value + */ +android_atomic_acquire_swap: + .fnstart +1: ldrex r2, [r1] @ load current value into r2 + strex r3, r0, [r1] @ store new value + teq r3, #0 @ strex success? + bne 1b @ no, loop + mov r0, r2 @ return old value + SMP_DMB + bx lr + .fnend + +android_atomic_release_swap: + .fnstart + SMP_DMB +1: ldrex r2, [r1] + strex r3, r0, [r1] + teq r3, #0 + bne 1b + mov r0, r2 + bx lr + .fnend + + +/* + * android_atomic_acquire_cas / android_atomic_release_cas + * input: r0 = oldvalue, r1 = newvalue, r2 = address + * output: r0 = 0 (xchg done) or non-zero (xchg not done) + */ +android_atomic_acquire_cas: + .fnstart +1: mov ip, #2 @ ip=2 means "new != old" + ldrex r3, [r2] @ load current value into r3 + teq r0, r3 @ new == old? + strexeq ip, r1, [r2] @ yes, try store, set ip to 0 or 1 + teq ip, #1 @ strex failure? + beq 1b @ yes, retry + mov r0, ip @ return 0 on success, 2 on failure + SMP_DMB + bx lr + .fnend + +android_atomic_release_cas: + .fnstart + SMP_DMB +1: mov ip, #2 + ldrex r3, [r2] + teq r0, r3 + strexeq ip, r1, [r2] + teq ip, #1 + beq 1b + mov r0, ip + bx lr + .fnend + + +#else /*not defined __ARM_HAVE_LDREX_STREX*/ +/* + * =========================================================================== + * Pre-ARMv6 implementation + * =========================================================================== + * + * These functions call through the kernel cmpxchg facility, or use the + * (now deprecated) SWP instruction. They are not SMP-safe. + */ +#if ANDROID_SMP == 1 +# error "SMP defined, but LDREX/STREX not available" +#endif + +/* * int __kernel_cmpxchg(int oldval, int newval, int *ptr) * clobbered: r3, ip, flags * return 0 if a swap was made, non-zero otherwise. */ - .equ kernel_cmpxchg, 0xFFFF0FC0 .equ kernel_atomic_base, 0xFFFF0FFF + +/* + * android_atomic_acquire_load / android_atomic_release_load + * input: r0 = address + * output: r0 = value + */ +android_atomic_acquire_load: +android_atomic_release_load: + .fnstart + ldr r0, [r0] + bx lr + .fnend + + /* - * ---------------------------------------------------------------------------- - * android_atomic_write - * input: r0=value, r1=address + * android_atomic_acquire_store / android_atomic_release_store + * input: r0 = value, r1 = address * output: void */ - -android_atomic_write: +android_atomic_acquire_store: +android_atomic_release_store: + .fnstart str r0, [r1] - bx lr; + bx lr + .fnend + /* - * ---------------------------------------------------------------------------- * android_atomic_inc * input: r0 = address * output: r0 = old value */ - android_atomic_inc: .fnstart .save {r4, lr} @@ -95,14 +384,13 @@ android_atomic_inc: ldmia sp!, {r4, lr} bx lr .fnend - + + /* - * ---------------------------------------------------------------------------- * android_atomic_dec - * input: r0=address + * input: r0 = address * output: r0 = old value */ - android_atomic_dec: .fnstart .save {r4, lr} @@ -126,14 +414,13 @@ android_atomic_dec: ldmia sp!, {r4, lr} bx lr .fnend - + + /* - * ---------------------------------------------------------------------------- * android_atomic_add - * input: r0=value, r1=address + * input: r0 = value, r1 = address * output: r0 = old value */ - android_atomic_add: .fnstart .save {r4, lr} @@ -158,19 +445,17 @@ android_atomic_add: ldmia sp!, {r4, lr} bx lr .fnend - - + + /* - * ---------------------------------------------------------------------------- * android_atomic_and - * input: r0=value, r1=address + * input: r0 = value, r1 = address * output: r0 = old value */ - android_atomic_and: .fnstart - .save {r4, r5, lr} - stmdb sp!, {r4, r5, lr} + .save {r4, r5, ip, lr} /* include ip for 64-bit stack alignment */ + stmdb sp!, {r4, r5, ip, lr} mov r2, r1 /* r2 = address */ mov r4, r0 /* r4 = the value */ 1: @ android_atomic_and @@ -190,21 +475,20 @@ android_atomic_and: #endif bcc 1b mov r0, r5 - ldmia sp!, {r4, r5, lr} + ldmia sp!, {r4, r5, ip, lr} bx lr .fnend - + + /* - * ---------------------------------------------------------------------------- * android_atomic_or - * input: r0=value, r1=address + * input: r0 = value, r1 = address * output: r0 = old value */ - android_atomic_or: .fnstart - .save {r4, r5, lr} - stmdb sp!, {r4, r5, lr} + .save {r4, r5, ip, lr} /* include ip for 64-bit stack alignment */ + stmdb sp!, {r4, r5, ip, lr} mov r2, r1 /* r2 = address */ mov r4, r0 /* r4 = the value */ 1: @ android_atomic_or @@ -224,39 +508,31 @@ android_atomic_or: #endif bcc 1b mov r0, r5 - ldmia sp!, {r4, r5, lr} + ldmia sp!, {r4, r5, ip, lr} bx lr .fnend + /* - * ---------------------------------------------------------------------------- - * android_atomic_swap - * input: r0=value, r1=address + * android_atomic_acquire_swap / android_atomic_release_swap + * input: r0 = value, r1 = address * output: r0 = old value */ - -/* replaced swp instruction with ldrex/strex for ARMv6 & ARMv7 */ -android_atomic_swap: -#if defined (_ARM_HAVE_LDREX_STREX) -1: ldrex r2, [r1] - strex r3, r0, [r1] - teq r3, #0 - bne 1b - mov r0, r2 - mcr p15, 0, r0, c7, c10, 5 /* or, use dmb */ -#else +android_atomic_acquire_swap: +android_atomic_release_swap: + .fnstart swp r0, r0, [r1] -#endif bx lr + .fnend + /* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg - * input: r0=oldvalue, r1=newvalue, r2=address + * android_atomic_acquire_cas / android_atomic_release_cas + * input: r0 = oldvalue, r1 = newvalue, r2 = address * output: r0 = 0 (xchg done) or non-zero (xchg not done) */ - -android_atomic_cmpxchg: +android_atomic_acquire_cas: +android_atomic_release_cas: .fnstart .save {r4, lr} stmdb sp!, {r4, lr} @@ -282,10 +558,4 @@ android_atomic_cmpxchg: bx lr .fnend -/* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg_64 - * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address - * output: r0 = 0 (xchg done) or non-zero (xchg not done) - */ -/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */ +#endif /*not defined __ARM_HAVE_LDREX_STREX*/ diff --git a/libcutils/atomic-android-armv6.S b/libcutils/atomic-android-armv6.S deleted file mode 100644 index 1574c9c..0000000 --- a/libcutils/atomic-android-armv6.S +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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. - */ - - - .text - .align - - .global android_atomic_write - .type android_atomic_write, %function - - .global android_atomic_inc - .type android_atomic_inc, %function - .global android_atomic_dec - .type android_atomic_dec, %function - - .global android_atomic_add - .type android_atomic_add, %function - .global android_atomic_and - .type android_atomic_and, %function - .global android_atomic_or - .type android_atomic_or, %function - - .global android_atomic_swap - .type android_atomic_swap, %function - - .global android_atomic_cmpxchg - .type android_atomic_cmpxchg, %function - - - -/* FIXME: On SMP systems memory barriers may be needed */ -#warning "this file is not safe with SMP systems" - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_write - * input: r0=value, r1=address - * output: void - */ - -android_atomic_write: - str r0, [r1] - bx lr; - -/* - * ---------------------------------------------------------------------------- - * android_atomic_inc - * input: r0 = address - * output: r0 = old value - */ - -android_atomic_inc: - mov r12, r0 -1: ldrex r0, [r12] - add r2, r0, #1 - strex r1, r2, [r12] - cmp r1, #0 - bxeq lr - b 1b - -/* - * ---------------------------------------------------------------------------- - * android_atomic_dec - * input: r0=address - * output: r0 = old value - */ - -android_atomic_dec: - mov r12, r0 -1: ldrex r0, [r12] - sub r2, r0, #1 - strex r1, r2, [r12] - cmp r1, #0 - bxeq lr - b 1b - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_add - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_add: - mov r12, r0 -1: ldrex r0, [r1] - add r2, r0, r12 - strex r3, r2, [r1] - cmp r3, #0 - bxeq lr - b 1b - -/* - * ---------------------------------------------------------------------------- - * android_atomic_and - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_and: - mov r12, r0 -1: ldrex r0, [r1] - and r2, r0, r12 - strex r3, r2, [r1] - cmp r3, #0 - bxeq lr - b 1b - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_or - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_or: - mov r12, r0 -1: ldrex r0, [r1] - orr r2, r0, r12 - strex r3, r2, [r1] - cmp r3, #0 - bxeq lr - b 1b - -/* - * ---------------------------------------------------------------------------- - * android_atomic_swap - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_swap: - swp r0, r0, [r1] - bx lr - -/* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg - * input: r0=oldvalue, r1=newvalue, r2=address - * output: r0 = 0 (xchg done) or non-zero (xchg not done) - */ - -android_atomic_cmpxchg: - mov r12, r1 - ldrex r3, [r2] - eors r0, r0, r3 - strexeq r0, r12, [r2] - bx lr - - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_cmpxchg_64 - * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address - * output: r0 = 0 (xchg done) or non-zero (xchg not done) - */ -/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */ diff --git a/libcutils/atomic-android-sh.c b/libcutils/atomic-android-sh.c index acbea97..abe7d25 100644 --- a/libcutils/atomic-android-sh.c +++ b/libcutils/atomic-android-sh.c @@ -35,6 +35,9 @@ * ARM implementation, in this file above. * We follow the fact that the initializer for mutex is a simple zero * value. + * + * (3) These operations are NOT safe for SMP, as there is no currently + * no definition for a memory barrier operation. */ #include <pthread.h> @@ -46,18 +49,35 @@ static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT]; &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT] -void android_atomic_write(int32_t value, volatile int32_t* addr) { +int32_t android_atomic_acquire_load(volatile int32_t* addr) +{ + return *addr; +} + +int32_t android_atomic_release_load(volatile int32_t* addr) +{ + return *addr; +} + +void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, value, addr)); + } while (android_atomic_release_cas(oldValue, value, addr)); +} + +void android_atomic_release_store(int32_t value, volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (android_atomic_release_cas(oldValue, value, addr)); } int32_t android_atomic_inc(volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr)); + } while (android_atomic_release_cas(oldValue, oldValue+1, addr)); return oldValue; } @@ -65,7 +85,7 @@ int32_t android_atomic_dec(volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue-1, addr)); + } while (android_atomic_release_cas(oldValue, oldValue-1, addr)); return oldValue; } @@ -73,7 +93,7 @@ int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue+value, addr)); + } while (android_atomic_release_cas(oldValue, oldValue+value, addr)); return oldValue; } @@ -81,7 +101,7 @@ int32_t android_atomic_and(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue&value, addr)); + } while (android_atomic_release_cas(oldValue, oldValue&value, addr)); return oldValue; } @@ -89,11 +109,15 @@ int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue|value, addr)); + } while (android_atomic_release_cas(oldValue, oldValue|value, addr)); return oldValue; } -int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) { +int32_t android_atomic_acquire_swap(int32_t value, volatile int32_t* addr) { + return android_atomic_release_swap(value, addr); +} + +int32_t android_atomic_release_swap(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; @@ -101,38 +125,13 @@ int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) { return oldValue; } -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, +int android_atomic_acquire_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { - int result; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - - if (*addr == oldvalue) { - *addr = newvalue; - result = 0; - } else { - result = 1; - } - pthread_mutex_unlock(lock); - return result; + return android_atomic_release_cmpxchg(oldValue, newValue, addr); } -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) { - int64_t oldValue; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - - oldValue = *addr; - *addr = value; - - pthread_mutex_unlock(lock); - return oldValue; -} - -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr) { +int android_atomic_release_cmpxchg(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr) { int result; pthread_mutex_t* lock = SWAP_LOCK(addr); @@ -148,12 +147,3 @@ int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, return result; } -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - int64_t result; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - result = *addr; - pthread_mutex_unlock(lock); - return result; -} diff --git a/libcutils/atomic.c b/libcutils/atomic.c index 41faaa2..4cefa6b 100644 --- a/libcutils/atomic.c +++ b/libcutils/atomic.c @@ -15,6 +15,7 @@ */ #include <cutils/atomic.h> +#include <cutils/atomic-inline.h> #ifdef HAVE_WIN32_THREADS #include <windows.h> #else @@ -26,11 +27,25 @@ #include <libkern/OSAtomic.h> -void android_atomic_write(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (OSAtomicCompareAndSwap32Barrier(oldValue, value, (int32_t*)addr) == 0); +int32_t android_atomic_acquire_load(volatile int32_t* addr) { + int32_t value = *addr; + OSMemoryBarrier(); + return value; +} + +int32_t android_atomic_release_load(volatile int32_t* addr) { + OSMemoryBarrier(); + return *addr; +} + +void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) { + *addr = value; + OSMemoryBarrier(); +} + +void android_atomic_release_store(int32_t value, volatile int32_t* addr) { + OSMemoryBarrier(); + *addr = value; } int32_t android_atomic_inc(volatile int32_t* addr) { @@ -46,95 +61,81 @@ int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { } int32_t android_atomic_and(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue&value, (int32_t*)addr) == 0); - return oldValue; + return OSAtomicAnd32OrigBarrier(value, (int32_t*)addr); } int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { + return OSAtomicOr32OrigBarrier(value, (int32_t*)addr); +} + +int32_t android_atomic_acquire_swap(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue|value, (int32_t*)addr) == 0); + } while (android_atomic_acquire_cas(oldValue, value, addr)); return oldValue; } -int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) { +int32_t android_atomic_release_swap(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, value, addr)); + } while (android_atomic_release_cas(oldValue, value, addr)); return oldValue; } -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { +int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { + /* OS X CAS returns zero on failure; invert to return zero on success */ return OSAtomicCompareAndSwap32Barrier(oldvalue, newvalue, (int32_t*)addr) == 0; } -#if defined(__ppc__) \ - || defined(__PPC__) \ - || defined(__powerpc__) \ - || defined(__powerpc) \ - || defined(__POWERPC__) \ - || defined(_M_PPC) \ - || defined(__PPC) -#define NEED_QUASIATOMICS 1 -#else - -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr) { - return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue, - (int64_t*)addr) == 0; -} - -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) { - int64_t oldValue; - do { - oldValue = *addr; - } while (android_quasiatomic_cmpxchg_64(oldValue, value, addr)); - return oldValue; +int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr) { + int result = (OSAtomicCompareAndSwap32(oldvalue, newvalue, (int32_t*)addr) == 0); + if (result == 0) { + /* success, perform barrier */ + OSMemoryBarrier(); + } + return result; } -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - return OSAtomicAdd64Barrier(0, addr); -} +/*****************************************************************************/ +#elif defined(__i386__) || defined(__x86_64__) -#endif +int32_t android_atomic_acquire_load(volatile int32_t* addr) { + int32_t value = *addr; + ANDROID_MEMBAR_FULL(); + return value; +} +int32_t android_atomic_release_load(volatile int32_t* addr) { + ANDROID_MEMBAR_FULL(); + return *addr; +} -/*****************************************************************************/ -#elif defined(__i386__) || defined(__x86_64__) +void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) { + *addr = value; + ANDROID_MEMBAR_FULL(); +} -void android_atomic_write(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, value, addr)); +void android_atomic_release_store(int32_t value, volatile int32_t* addr) { + ANDROID_MEMBAR_FULL(); + *addr = value; } int32_t android_atomic_inc(volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr)); - return oldValue; + return android_atomic_add(1, addr); } int32_t android_atomic_dec(volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue-1, addr)); - return oldValue; + return android_atomic_add(-1, addr); } int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue+value, addr)); + } while (android_atomic_release_cas(oldValue, oldValue+value, addr)); return oldValue; } @@ -142,7 +143,7 @@ int32_t android_atomic_and(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue&value, addr)); + } while (android_atomic_release_cas(oldValue, oldValue&value, addr)); return oldValue; } @@ -150,19 +151,13 @@ int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { int32_t oldValue; do { oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, oldValue|value, addr)); - return oldValue; -} - -int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(oldValue, value, addr)); + } while (android_atomic_release_cas(oldValue, oldValue|value, addr)); return oldValue; } -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { +/* returns 0 on successful swap */ +static inline int cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr) { int xchg; asm volatile ( @@ -175,75 +170,43 @@ int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* return xchg; } -#define NEED_QUASIATOMICS 1 - -/*****************************************************************************/ -#elif __arm__ -// Most of the implementation is in atomic-android-arm.s. - -// on the device, we implement the 64-bit atomic operations through -// mutex locking. normally, this is bad because we must initialize -// a pthread_mutex_t before being able to use it, and this means -// having to do an initialization check on each function call, and -// that's where really ugly things begin... -// -// BUT, as a special twist, we take advantage of the fact that in our -// pthread library, a mutex is simply a volatile word whose value is always -// initialized to 0. In other words, simply declaring a static mutex -// object initializes it ! -// -// another twist is that we use a small array of mutexes to dispatch -// the contention locks from different memory addresses -// - -#include <pthread.h> - -#define SWAP_LOCK_COUNT 32U -static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT]; - -#define SWAP_LOCK(addr) \ - &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT] - - -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) { - int64_t oldValue; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - - oldValue = *addr; - *addr = value; - - pthread_mutex_unlock(lock); +int32_t android_atomic_acquire_swap(int32_t value, volatile int32_t* addr) { + int32_t oldValue; + do { + oldValue = *addr; + } while (cas(oldValue, value, addr)); + ANDROID_MEMBAR_FULL(); return oldValue; } -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr) { - int result; - pthread_mutex_t* lock = SWAP_LOCK(addr); +int32_t android_atomic_release_swap(int32_t value, volatile int32_t* addr) { + ANDROID_MEMBAR_FULL(); + int32_t oldValue; + do { + oldValue = *addr; + } while (cas(oldValue, value, addr)); + return oldValue; +} - pthread_mutex_lock(lock); +int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr) { + int xchg = cas(oldvalue, newvalue, addr); + if (xchg == 0) + ANDROID_MEMBAR_FULL(); + return xchg; +} - if (*addr == oldvalue) { - *addr = newvalue; - result = 0; - } else { - result = 1; - } - pthread_mutex_unlock(lock); - return result; +int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr) { + ANDROID_MEMBAR_FULL(); + int xchg = cas(oldvalue, newvalue, addr); + return xchg; } -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - int64_t result; - pthread_mutex_t* lock = SWAP_LOCK(addr); - pthread_mutex_lock(lock); - result = *addr; - pthread_mutex_unlock(lock); - return result; -} +/*****************************************************************************/ +#elif __arm__ +// implementation for ARM is in atomic-android-arm.s. /*****************************************************************************/ #elif __sh__ @@ -255,85 +218,3 @@ int64_t android_quasiatomic_read_64(volatile int64_t* addr) { #endif - - -#if NEED_QUASIATOMICS - -/* Note that a spinlock is *not* a good idea in general - * since they can introduce subtle issues. For example, - * a real-time thread trying to acquire a spinlock already - * acquired by another thread will never yeld, making the - * CPU loop endlessly! - * - * However, this code is only used on the Linux simulator - * so it's probably ok for us. - * - * The alternative is to use a pthread mutex, but - * these must be initialized before being used, and - * then you have the problem of lazily initializing - * a mutex without any other synchronization primitive. - */ - -/* global spinlock for all 64-bit quasiatomic operations */ -static int32_t quasiatomic_spinlock = 0; - -int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue, - volatile int64_t* addr) { - int result; - - while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) { -#ifdef HAVE_WIN32_THREADS - Sleep(0); -#else - sched_yield(); -#endif - } - - if (*addr == oldvalue) { - *addr = newvalue; - result = 0; - } else { - result = 1; - } - - android_atomic_swap(0, &quasiatomic_spinlock); - - return result; -} - -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - int64_t result; - - while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) { -#ifdef HAVE_WIN32_THREADS - Sleep(0); -#else - sched_yield(); -#endif - } - - result = *addr; - android_atomic_swap(0, &quasiatomic_spinlock); - - return result; -} - -int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) { - int64_t result; - - while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) { -#ifdef HAVE_WIN32_THREADS - Sleep(0); -#else - sched_yield(); -#endif - } - - result = *addr; - *addr = value; - android_atomic_swap(0, &quasiatomic_spinlock); - - return result; -} - -#endif diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c index b247016..ff00432 100644 --- a/libnetutils/dhcpclient.c +++ b/libnetutils/dhcpclient.c @@ -70,7 +70,7 @@ void printerr(char *fmt, ...) vsnprintf(errmsg, sizeof(errmsg), fmt, ap); va_end(ap); - LOGD(errmsg); + LOGD("%s", errmsg); } const char *dhcp_lasterror() diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp index 29410c8..5877ff4 100644 --- a/libpixelflinger/codeflinger/CodeCache.cpp +++ b/libpixelflinger/codeflinger/CodeCache.cpp @@ -19,6 +19,8 @@ #include <assert.h> #include <stdio.h> #include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> #include <cutils/log.h> #include <cutils/atomic.h> @@ -39,15 +41,14 @@ namespace android { Assembly::Assembly(size_t size) : mCount(1), mSize(0) { - mBase = (uint32_t*)malloc(size); - if (mBase) { - mSize = size; - } + mBase = (uint32_t*)mspace_malloc(getMspace(), size); + mSize = size; + ensureMbaseExecutable(); } Assembly::~Assembly() { - free(mBase); + mspace_free(getMspace(), mBase); } void Assembly::incStrong(const void*) const @@ -75,11 +76,32 @@ uint32_t* Assembly::base() const ssize_t Assembly::resize(size_t newSize) { - mBase = (uint32_t*)realloc(mBase, newSize); + mBase = (uint32_t*)mspace_realloc(getMspace(), mBase, newSize); mSize = newSize; + ensureMbaseExecutable(); return size(); } +mspace Assembly::getMspace() +{ + static mspace msp = create_contiguous_mspace(2 * 1024, 1024 * 1024, /*locked=*/ false); + return msp; +} + +void Assembly::ensureMbaseExecutable() +{ + long pagesize = sysconf(_SC_PAGESIZE); + long pagemask = ~(pagesize - 1); // assumes pagesize is a power of 2 + + uint32_t* pageStart = (uint32_t*) (((uintptr_t) mBase) & pagemask); + size_t adjustedLength = mBase - pageStart + mSize; + + if (mBase && mprotect(pageStart, adjustedLength, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { + mspace_free(getMspace(), mBase); + mBase = NULL; + } +} + // ---------------------------------------------------------------------------- CodeCache::CodeCache(size_t size) diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h index 8ff1366..aaafd26 100644 --- a/libpixelflinger/codeflinger/CodeCache.h +++ b/libpixelflinger/codeflinger/CodeCache.h @@ -22,6 +22,7 @@ #include <stdint.h> #include <pthread.h> #include <sys/types.h> +#include <cutils/mspace.h> #include "tinyutils/KeyedVector.h" #include "tinyutils/smartpointer.h" @@ -67,9 +68,12 @@ public: typedef void weakref_type; private: + static mspace getMspace(); + void ensureMbaseExecutable(); + mutable int32_t mCount; uint32_t* mBase; - ssize_t mSize; + size_t mSize; }; // ---------------------------------------------------------------------------- diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c index 6466795..bdf53e8 100644 --- a/logwrapper/logwrapper.c +++ b/logwrapper/logwrapper.c @@ -27,8 +27,8 @@ #include "cutils/log.h" void fatal(const char *msg) { - fprintf(stderr, msg); - LOG(LOG_ERROR, "logwrapper", msg); + fprintf(stderr, "%s", msg); + LOG(LOG_ERROR, "logwrapper", "%s", msg); exit(-1); } diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 7ac991c..329be7f 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -37,8 +37,15 @@ file := $(TARGET_ROOT_OUT)/init.rc $(file) : $(LOCAL_PATH)/init.rc | $(ACP) $(transform-prebuilt-to-target) ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) endif +file := $(TARGET_ROOT_OUT)/ueventd.rc +$(file) : $(LOCAL_PATH)/ueventd.rc | $(ACP) + $(transform-prebuilt-to-target) +ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) + # Just like /system/etc/init.goldfish.sh, the /init.godlfish.rc is here # to allow -user builds to properly run the dex pre-optimization pass in # the emulator. @@ -46,6 +53,13 @@ file := $(TARGET_ROOT_OUT)/init.goldfish.rc $(file) : $(LOCAL_PATH)/etc/init.goldfish.rc | $(ACP) $(transform-prebuilt-to-target) ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) + +file := $(TARGET_ROOT_OUT)/ueventd.goldfish.rc +$(file) : $(LOCAL_PATH)/etc/ueventd.goldfish.rc | $(ACP) + $(transform-prebuilt-to-target) +ALL_PREBUILT += $(file) +$(INSTALLED_RAMDISK_TARGET): $(file) # create some directories (some are mount points) DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \ diff --git a/rootdir/etc/ueventd.goldfish.rc b/rootdir/etc/ueventd.goldfish.rc new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/rootdir/etc/ueventd.goldfish.rc diff --git a/rootdir/init.lowmem.rc b/rootdir/init.lowmem.rc new file mode 100644 index 0000000..7c08054 --- /dev/null +++ b/rootdir/init.lowmem.rc @@ -0,0 +1,19 @@ +# Adjustments to the out-of-memory killer, for devices that are +# tight on memory. These should not be used if not needed, as they +# can result in more paging. + +on early-boot + + setprop ro.FOREGROUND_APP_MEM 1536 + setprop ro.VISIBLE_APP_MEM 2048 + setprop ro.PERCEPTIBLE_APP_MEM 2048 + setprop ro.HEAVY_WEIGHT_APP_MEM 2048 + setprop ro.SECONDARY_SERVER_MEM 4096 + setprop ro.BACKUP_APP_MEM 4096 + setprop ro.HOME_APP_MEM 4096 + setprop ro.HIDDEN_APP_MEM 5120 + setprop ro.EMPTY_APP_MEM 6144 + +on boot + + write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,3072,4096,5120,6144 diff --git a/rootdir/init.rc b/rootdir/init.rc index 1e446f7..d2ee6d5 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -1,3 +1,5 @@ +on early-init + start ueventd on init @@ -80,13 +82,16 @@ loglevel 3 # 5.0 % write /dev/cpuctl/bg_non_interactive/cpu.shares 52 +on fs # mount mtd partitions # Mount /system rw first to give the filesystem a chance to save a checkpoint mount yaffs2 mtd@system /system mount yaffs2 mtd@system /system ro remount + mount yaffs2 mtd@userdata /data nosuid nodev + mount yaffs2 mtd@cache /cache nosuid nodev +on post-fs # We chown/chmod /data again so because mount is run as root + defaults - mount yaffs2 mtd@userdata /data nosuid nodev chown system system /data chmod 0771 /data @@ -110,7 +115,6 @@ loglevel 3 write /proc/apanic_console 1 # Same reason as /data above - mount yaffs2 mtd@cache /cache nosuid nodev chown system cache /cache chmod 0770 /cache @@ -175,32 +179,35 @@ on boot # killed by the kernel. These are used in ActivityManagerService. setprop ro.FOREGROUND_APP_ADJ 0 setprop ro.VISIBLE_APP_ADJ 1 - setprop ro.SECONDARY_SERVER_ADJ 2 - setprop ro.BACKUP_APP_ADJ 2 - setprop ro.HOME_APP_ADJ 4 + setprop ro.PERCEPTIBLE_APP_ADJ 2 + setprop ro.HEAVY_WEIGHT_APP_ADJ 3 + setprop ro.SECONDARY_SERVER_ADJ 4 + setprop ro.BACKUP_APP_ADJ 5 + setprop ro.HOME_APP_ADJ 6 setprop ro.HIDDEN_APP_MIN_ADJ 7 - setprop ro.CONTENT_PROVIDER_ADJ 14 setprop ro.EMPTY_APP_ADJ 15 # Define the memory thresholds at which the above process classes will # be killed. These numbers are in pages (4k). - setprop ro.FOREGROUND_APP_MEM 1536 - setprop ro.VISIBLE_APP_MEM 2048 - setprop ro.SECONDARY_SERVER_MEM 4096 - setprop ro.BACKUP_APP_MEM 4096 - setprop ro.HOME_APP_MEM 4096 - setprop ro.HIDDEN_APP_MEM 5120 - setprop ro.CONTENT_PROVIDER_MEM 5632 - setprop ro.EMPTY_APP_MEM 6144 + setprop ro.FOREGROUND_APP_MEM 2048 + setprop ro.VISIBLE_APP_MEM 3072 + setprop ro.PERCEPTIBLE_APP_MEM 4096 + setprop ro.HEAVY_WEIGHT_APP_MEM 4096 + setprop ro.SECONDARY_SERVER_MEM 6144 + setprop ro.BACKUP_APP_MEM 6144 + setprop ro.HOME_APP_MEM 6144 + setprop ro.HIDDEN_APP_MEM 7168 + setprop ro.EMPTY_APP_MEM 8192 # Write value must be consistent with the above properties. -# Note that the driver only supports 6 slots, so we have HOME_APP at the -# same memory level as services. - write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15 +# Note that the driver only supports 6 slots, so we have combined some of +# the classes into the same memory level; the associated processes of higher +# classes will still be killed first. + write /sys/module/lowmemorykiller/parameters/adj 0,1,2,4,7,15 write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 - write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144 + write /sys/module/lowmemorykiller/parameters/minfree 2048,3072,4096,6144,7168,8192 # Set init its forked children's oom_adj. write /proc/1/oom_adj -16 @@ -263,6 +270,9 @@ on boot service console /system/bin/sh console +service ueventd /sbin/ueventd + critical + # adbd is controlled by the persist.service.adb.enable system property service adbd /sbin/adbd disabled diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc new file mode 100644 index 0000000..7845eb9 --- /dev/null +++ b/rootdir/ueventd.rc @@ -0,0 +1,77 @@ +/dev/null 0666 root root +/dev/zero 0666 root root +/dev/full 0666 root root +/dev/ptmx 0666 root root +/dev/tty 0666 root root +/dev/random 0666 root root +/dev/urandom 0666 root root +/dev/ashmem 0666 root root +/dev/binder 0666 root root + +# logger should be world writable (for logging) but not readable +/dev/log/* 0662 root log + +# the msm hw3d client device node is world writable/readable. +/dev/msm_hw3dc 0666 root root + +# gpu driver for adreno200 is globally accessible +/dev/kgsl 0666 root root + +# these should not be world writable +/dev/diag 0660 radio radio +/dev/diag_arm9 0660 radio radio +/dev/android_adb 0660 adb adb +/dev/android_adb_enable 0660 adb adb +/dev/ttyMSM0 0600 bluetooth bluetooth +/dev/ttyHS0 0600 bluetooth bluetooth +/dev/uinput 0660 system bluetooth +/dev/alarm 0664 system radio +/dev/tty0 0660 root system +/dev/graphics/* 0660 root graphics +/dev/msm_hw3dm 0660 system graphics +/dev/input/* 0660 root input +/dev/eac 0660 root audio +/dev/cam 0660 root camera +/dev/pmem 0660 system graphics +/dev/pmem_adsp* 0660 system audio +/dev/pmem_camera* 0660 system camera +/dev/oncrpc/* 0660 root system +/dev/adsp/* 0660 system audio +/dev/snd/* 0660 system audio +/dev/mt9t013 0660 system system +/dev/msm_camera/* 0660 system system +/dev/akm8976_daemon 0640 compass system +/dev/akm8976_aot 0640 compass system +/dev/akm8973_daemon 0640 compass system +/dev/akm8973_aot 0640 compass system +/dev/bma150 0640 compass system +/dev/cm3602 0640 compass system +/dev/akm8976_pffd 0640 compass system +/dev/lightsensor 0640 system system +/dev/msm_pcm_out* 0660 system audio +/dev/msm_pcm_in* 0660 system audio +/dev/msm_pcm_ctl* 0660 system audio +/dev/msm_snd* 0660 system audio +/dev/msm_mp3* 0660 system audio +/dev/audience_a1026* 0660 system audio +/dev/tpa2018d1* 0660 system audio +/dev/msm_audpre 0660 system audio +/dev/msm_audio_ctl 0660 system audio +/dev/htc-acoustic 0660 system audio +/dev/vdec 0660 system audio +/dev/q6venc 0660 system audio +/dev/snd/dsp 0660 system audio +/dev/snd/dsp1 0660 system audio +/dev/snd/mixer 0660 system audio +/dev/smd0 0640 radio radio +/dev/qemu_trace 0666 system system +/dev/qmi 0640 radio radio +/dev/qmi0 0640 radio radio +/dev/qmi1 0640 radio radio +/dev/qmi2 0640 radio radio +/dev/bus/usb/* 0660 root usb + +# CDMA radio interface MUX +/dev/ts0710mux* 0640 radio radio +/dev/ppp 0660 radio vpn +/dev/tun 0640 vpn vpn diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 05b2a34..9349276 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -53,7 +53,7 @@ TOOLS := \ uptime \ vmstat \ nandread \ - ionice + ionice LOCAL_SRC_FILES:= \ toolbox.c \ |