diff options
69 files changed, 4835 insertions, 2359 deletions
diff --git a/ThirdPartyProject.prop b/ThirdPartyProject.prop new file mode 100644 index 0000000..18b0594 --- /dev/null +++ b/ThirdPartyProject.prop @@ -0,0 +1,10 @@ +# Copyright 2010 Google Inc. All Rights Reserved. +#Fri Jul 16 10:03:09 PDT 2010 +currentVersion=2.6.32 +version=2.6.32 +isNative=true +feedurl=http\://kernel.org/pub/linux/kernel/v2.6/ +name=linux +keywords=linux +onDevice=true +homepage=http\://kernel.org diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 3b2972a..24b7e72 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -375,7 +375,7 @@ static void parse_elf_info(mapinfo *milist, pid_t pid) ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff); for (i = 0; i < ehdr.e_phnum; i++) { /* Parse the program header */ - get_remote_struct(pid, (char *) ptr+i, &phdr, + get_remote_struct(pid, (char *) (ptr+i), &phdr, sizeof(Elf32_Phdr)); /* Found a EXIDX segment? */ if (phdr.p_type == PT_ARM_EXIDX) { diff --git a/fastboot/engine.c b/fastboot/engine.c index dc74417..48073ee 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -291,7 +291,8 @@ void fb_execute_queue(usb_handle *usb) a->start = now(); if (start < 0) start = a->start; if (a->msg) { - fprintf(stderr,"%30s... ",a->msg); + // fprintf(stderr,"%30s... ",a->msg); + fprintf(stderr,"%s...\n",a->msg); } if (a->op == OP_DOWNLOAD) { status = fb_download_data(usb, a->data, a->size); 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/protocol.c b/fastboot/protocol.c index c788a12..3948363 100644 --- a/fastboot/protocol.c +++ b/fastboot/protocol.c @@ -62,7 +62,7 @@ static int check_response(usb_handle *usb, unsigned size, } if(!memcmp(status, "INFO", 4)) { - fprintf(stderr,"%s\n", status); + fprintf(stderr,"(bootloader) %s\n", status + 4); continue; } diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index 2ce53eb..78b7b98 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -137,6 +137,7 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, ctrl.wIndex = 0; ctrl.wLength = sizeof(buffer); ctrl.data = buffer; + ctrl.timeout = 50; result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); if (result > 0) { 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/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h index f51ddb1..32b7026 100644 --- a/include/arch/linux-arm/AndroidConfig.h +++ b/include/arch/linux-arm/AndroidConfig.h @@ -42,9 +42,16 @@ #define HAVE_PTHREADS /* - * Do we have the futex syscall? + * Do we have pthread_setname_np()? + * + * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with + * the same name but different parameters, so we can't use that here.) */ +#define HAVE_ANDROID_PTHREAD_SETNAME_NP +/* + * Do we have the futex syscall? + */ #define HAVE_FUTEX /* diff --git a/include/arch/linux-sh/AndroidConfig.h b/include/arch/linux-sh/AndroidConfig.h index 5e93990..76ae7d7 100644 --- a/include/arch/linux-sh/AndroidConfig.h +++ b/include/arch/linux-sh/AndroidConfig.h @@ -42,9 +42,16 @@ #define HAVE_PTHREADS /* - * Do we have the futex syscall? + * Do we have pthread_setname_np()? + * + * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with + * the same name but different parameters, so we can't use that here.) */ +#define HAVE_ANDROID_PTHREAD_SETNAME_NP +/* + * Do we have the futex syscall? + */ #define HAVE_FUTEX /* diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h index c7434ca..2152d6a 100644 --- a/include/arch/target_linux-x86/AndroidConfig.h +++ b/include/arch/target_linux-x86/AndroidConfig.h @@ -28,9 +28,16 @@ #define HAVE_PTHREADS /* - * Do we have the futex syscall? + * Do we have pthread_setname_np()? + * + * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with + * the same name but different parameters, so we can't use that here.) */ +#define HAVE_ANDROID_PTHREAD_SETNAME_NP +/* + * Do we have the futex syscall? + */ #define HAVE_FUTEX /* diff --git a/include/cutils/ashmem.h b/include/cutils/ashmem.h index fd56dbe..25b233e 100644 --- a/include/cutils/ashmem.h +++ b/include/cutils/ashmem.h @@ -10,7 +10,7 @@ #ifndef _CUTILS_ASHMEM_H #define _CUTILS_ASHMEM_H -#include <stdint.h> +#include <stddef.h> #ifdef __cplusplus extern "C" { diff --git a/include/cutils/atomic-arm.h b/include/cutils/atomic-arm.h new file mode 100644 index 0000000..0dd629d --- /dev/null +++ b/include/cutils/atomic-arm.h @@ -0,0 +1,269 @@ +/* + * 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_ARM_H +#define ANDROID_CUTILS_ATOMIC_ARM_H + +#include <stdint.h> +#include <machine/cpu-features.h> + +extern inline void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern inline void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +#elif defined(__ARM_HAVE_DMB) +extern inline void android_memory_barrier(void) +{ + __asm__ __volatile__ ("dmb" : : : "memory"); +} +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline void android_memory_barrier(void) +{ + __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" + : : "r" (0) : "memory"); +} +#else +extern inline void android_memory_barrier(void) +{ + typedef void (kuser_memory_barrier)(void); + (*(kuser_memory_barrier *)0xffff0fa0)(); +} +#endif + +extern inline int32_t android_atomic_acquire_load(volatile int32_t *ptr) +{ + int32_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern inline int32_t android_atomic_release_load(volatile int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern inline void android_atomic_acquire_store(int32_t value, + volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern inline void android_atomic_release_store(int32_t value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +#if defined(__thumb__) +extern int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ("ldrex %0, [%3]\n" + "mov %1, #0\n" + "teq %0, %4\n" + "strexeq %1, %5, [%3]" + : "=&r" (prev), "=&r" (status), "+m"(*ptr) + : "r" (ptr), "Ir" (old_value), "r" (new_value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev != old_value; +} +#else +extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + typedef int (kuser_cmpxchg)(int32_t, int32_t, volatile int32_t *); + int32_t prev, status; + prev = *ptr; + do { + status = (*(kuser_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr); + if (__builtin_expect(status == 0, 1)) + return 0; + prev = *ptr; + } while (prev == old_value); + return 1; +} +#endif + +extern inline int android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + int status = android_atomic_cas(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern inline int android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas(old_value, new_value, ptr); +} + + +#if defined(__thumb__) +extern int32_t android_atomic_swap(int32_t new_value, + volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int32_t android_atomic_swap(int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ("ldrex %0, [%3]\n" + "strex %1, %4, [%3]" + : "=&r" (prev), "=&r" (status), "+m" (*ptr) + : "r" (ptr), "r" (new_value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + android_memory_barrier(); + return prev; +} +#else +extern inline int32_t android_atomic_swap(int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev; + __asm__ __volatile__ ("swp %0, %2, [%3]" + : "=&r" (prev), "+m" (*ptr) + : "r" (new_value), "r" (ptr) + : "cc"); + android_memory_barrier(); + return prev; +} +#endif + +#if defined(__thumb__) +extern int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "add %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (increment) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#else +extern inline int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev + increment, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#endif + +extern inline int32_t android_atomic_inc(volatile int32_t *addr) { + return android_atomic_add(1, addr); +} + +extern inline int32_t android_atomic_dec(volatile int32_t *addr) { + return android_atomic_add(-1, addr); +} + +#if defined(__thumb__) +extern int32_t android_atomic_and(int32_t value, volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "and %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#else +extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev & value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#endif + +#if defined(__thumb__) +extern int32_t android_atomic_or(int32_t value, volatile int32_t *ptr); +#elif defined(__ARM_HAVE_LDREX_STREX) +extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "orr %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#else +extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + prev = *ptr; + status = android_atomic_cas(prev, prev | value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} +#endif + +#endif /* ANDROID_CUTILS_ATOMIC_ARM_H */ diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h new file mode 100644 index 0000000..715e0aa --- /dev/null +++ b/include/cutils/atomic-inline.h @@ -0,0 +1,58 @@ +/* + * 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 + +#if defined(__arm__) +#include <cutils/atomic-arm.h> +#elif defined(__i386__) || defined(__x86_64__) +#include <cutils/atomic-x86.h> +#elif defined(__sh__) +/* implementation is in atomic-android-sh.c */ +#else +#error atomic operations are unsupported +#endif + +#if ANDROID_SMP == 0 +#define ANDROID_MEMBAR_FULL android_compiler_barrier +#else +#define ANDROID_MEMBAR_FULL android_memory_barrier +#endif + +#endif /* ANDROID_CUTILS_ATOMIC_INLINE_H */ diff --git a/include/cutils/atomic-x86.h b/include/cutils/atomic-x86.h new file mode 100644 index 0000000..06b643f --- /dev/null +++ b/include/cutils/atomic-x86.h @@ -0,0 +1,139 @@ +/* + * 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_X86_H +#define ANDROID_CUTILS_ATOMIC_X86_H + +#include <stdint.h> + +extern inline void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern inline void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern inline void android_memory_barrier(void) +{ + __asm__ __volatile__ ("mfence" : : : "memory"); +} +#endif + +extern inline int32_t android_atomic_acquire_load(volatile int32_t *ptr) { + int32_t value = *ptr; + android_compiler_barrier(); + return value; +} + +extern inline int32_t android_atomic_release_load(volatile int32_t *ptr) { + android_memory_barrier(); + return *ptr; +} + +extern inline void android_atomic_acquire_store(int32_t value, + volatile int32_t *ptr) { + *ptr = value; + android_memory_barrier(); +} + +extern inline void android_atomic_release_store(int32_t value, + volatile int32_t *ptr) { + android_compiler_barrier(); + *ptr = value; +} + +extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev != old_value; +} + +extern inline int android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + /* Loads are not reordered with other loads. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern inline int android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + /* Stores are not reordered with other stores. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern inline int32_t android_atomic_swap(int32_t new_value, + volatile int32_t *ptr) +{ + __asm__ __volatile__ ("xchgl %1, %0" + : "=r" (new_value) + : "m" (*ptr), "0" (new_value) + : "memory"); + /* new_value now holds the old value of *ptr */ + return new_value; +} + +extern inline int32_t android_atomic_add(int32_t increment, + volatile int32_t *ptr) +{ + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "+r" (increment), "+m" (*ptr) + : : "memory"); + /* increment now holds the old value of *ptr */ + return increment; +} + +extern inline int32_t android_atomic_inc(volatile int32_t *addr) { + return android_atomic_add(1, addr); +} + +extern inline int32_t android_atomic_dec(volatile int32_t *addr) { + return android_atomic_add(-1, addr); +} + +extern inline int32_t android_atomic_and(int32_t value, + volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev & value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev | value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_X86_H */ diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h index 5694d66..3866848 100644 --- a/include/cutils/atomic.h +++ b/include/cutils/atomic.h @@ -25,53 +25,102 @@ 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 release ordering. + * + * Stores the new value at *addr, and returns the previous value. */ +int32_t android_atomic_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/include/cutils/mspace.h b/include/cutils/mspace.h index e6e4047..93fe48e 100644 --- a/include/cutils/mspace.h +++ b/include/cutils/mspace.h @@ -87,6 +87,11 @@ mspace create_contiguous_mspace_with_base(size_t starting_capacity, size_t max_capacity, int locked, void *base); size_t destroy_contiguous_mspace(mspace msp); + +/* + Returns the position of the "break" within the given mspace. +*/ +void *contiguous_mspace_sbrk0(mspace msp); #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..8e912de 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; }; @@ -53,6 +58,7 @@ static int open_uevent_socket(void) { struct sockaddr_nl addr; int sz = 64*1024; // XXX larger? udev uses 16MB! + int on = 1; int s; memset(&addr, 0, sizeof(addr)); @@ -65,6 +71,7 @@ static int open_uevent_socket(void) return -1; setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); + setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(s); @@ -81,103 +88,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 +117,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); - 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++; + list_add_tail(&dev_perms, &node->plist); 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 +146,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 +223,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 +246,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 +264,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 +355,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 +414,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 +540,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,24 +554,52 @@ 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; + for(;;) { + char msg[UEVENT_MSG_LEN+2]; + char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + struct iovec iov = {msg, sizeof(msg)}; + struct sockaddr_nl snl; + struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0}; + + ssize_t n = recvmsg(device_fd, &hdr, 0); + if (n <= 0) { + break; + } - while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) { - struct uevent uevent; + if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) { + /* ignoring non-kernel netlink multicast message */ + continue; + } - if(n == UEVENT_MSG_LEN) /* overflow -- discard */ + struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr); + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + /* no sender credentials received, ignore message */ + continue; + } + + struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid != 0) { + /* message from non-root user, ignore */ + continue; + } + + if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ continue; msg[n] = '\0'; msg[n+1] = '\0'; + struct uevent uevent; parse_event(msg, &uevent); handle_device_event(&uevent); @@ -621,7 +616,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 +627,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 +644,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 0667593..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,199 +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 && *x != '\n') { - if (!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); @@ -853,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) @@ -1022,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..d8fea56 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -27,7 +27,6 @@ #include <cutils/misc.h> #include <cutils/sockets.h> -#include <cutils/ashmem.h> #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include <sys/_system_properties.h> @@ -43,10 +42,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 { @@ -105,21 +109,31 @@ static int init_workspace(workspace *w, size_t size) void *data; int fd; - fd = ashmem_create_region("system_properties", size); - if(fd < 0) + /* dev is a tmpfs that we can use to carve a shared workspace + * out of, so let's do that... + */ + fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600); + if (fd < 0) return -1; + if (ftruncate(fd, size) < 0) + goto out; + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(data == MAP_FAILED) goto out; - /* allow the wolves we share with to do nothing but read */ - ashmem_set_prot_region(fd, PROT_READ); + close(fd); + + fd = open("/dev/__properties__", O_RDONLY); + if (fd < 0) + return -1; + + unlink("/dev/__properties__"); w->data = data; w->size = size; w->fd = fd; - return 0; out: @@ -160,7 +174,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 +201,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 +222,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 +358,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 +369,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 +507,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 +523,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..d8ec88e 100644..100755 --- 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,247 @@ 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 && *x != '\n') { + if (!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..778b5bd 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -16,10 +16,17 @@ 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 \ - atomic.c \ + atomic.c.arm \ native_handle.c \ buffer.c \ socket_inaddr_any_server.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 @@ -103,7 +112,7 @@ LOCAL_MODULE := libcutils LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += memset32.S atomic-android-arm.S +LOCAL_SRC_FILES += memset32.S else # !arm ifeq ($(TARGET_ARCH),sh) LOCAL_SRC_FILES += memory.c atomic-android-sh.c @@ -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 deleted file mode 100644 index 1dd2363..0000000 --- a/libcutils/atomic-android-arm.S +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright (C) 2005 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 <machine/cpu-features.h> - -/* - * NOTE: these atomic operations are SMP safe on all architectures. - */ - - .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 - -/* - * ---------------------------------------------------------------------------- - * 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_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: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r2, r0 -1: @ android_atomic_inc - ldr r0, [r2] - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - add r1, r0, #1 - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - add r1, r0, #1 - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcc 1b - sub r0, r1, #1 - ldmia sp!, {r4, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_dec - * input: r0=address - * output: r0 = old value - */ - -android_atomic_dec: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r2, r0 -1: @ android_atomic_dec - ldr r0, [r2] - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - sub r1, r0, #1 - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - sub r1, r0, #1 - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcc 1b - add r0, r1, #1 - ldmia sp!, {r4, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_add - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_add: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r2, r1 - mov r4, r0 -1: @ android_atomic_add - ldr r0, [r2] - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - add r1, r0, r4 - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - add r1, r0, r4 - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcc 1b - sub r0, r1, r4 - ldmia sp!, {r4, lr} - bx lr - .fnend - - -/* - * ---------------------------------------------------------------------------- - * android_atomic_and - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_and: - .fnstart - .save {r4, r5, lr} - stmdb sp!, {r4, r5, lr} - mov r2, r1 /* r2 = address */ - mov r4, r0 /* r4 = the value */ -1: @ android_atomic_and - ldr r0, [r2] /* r0 = address[0] */ - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #8 - mov r5, r0 /* r5 = save address[0] */ - and r1, r0, r4 /* r1 = new value */ - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ -#else - mov r5, r0 /* r5 = save address[0] */ - and r1, r0, r4 /* r1 = new value */ - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ - mov lr, pc - bx r3 -#endif - bcc 1b - mov r0, r5 - ldmia sp!, {r4, r5, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_or - * input: r0=value, r1=address - * output: r0 = old value - */ - -android_atomic_or: - .fnstart - .save {r4, r5, lr} - stmdb sp!, {r4, r5, lr} - mov r2, r1 /* r2 = address */ - mov r4, r0 /* r4 = the value */ -1: @ android_atomic_or - ldr r0, [r2] /* r0 = address[0] */ - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #8 - mov r5, r0 /* r5 = save address[0] */ - orr r1, r0, r4 /* r1 = new value */ - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ -#else - mov r5, r0 /* r5 = save address[0] */ - orr r1, r0, r4 /* r1 = new value */ - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */ - mov lr, pc - bx r3 -#endif - bcc 1b - mov r0, r5 - ldmia sp!, {r4, r5, lr} - bx lr - .fnend - -/* - * ---------------------------------------------------------------------------- - * android_atomic_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 - swp r0, r0, [r1] -#endif - 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: - .fnstart - .save {r4, lr} - stmdb sp!, {r4, lr} - mov r4, r0 /* r4 = save oldvalue */ -1: @ android_atomic_cmpxchg - mov r3, #kernel_atomic_base -#ifdef __ARM_HAVE_PC_INTERWORK - add lr, pc, #4 - mov r0, r4 /* r0 = oldvalue */ - add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) -#else - mov r0, r4 /* r0 = oldvalue */ - add r3, r3, #(kernel_cmpxchg - kernel_atomic_base) - mov lr, pc - bx r3 -#endif - bcs 2f /* swap was made. we're good, return. */ - ldr r3, [r2] /* swap not made, see if it's because *ptr!=oldvalue */ - cmp r3, r4 - beq 1b -2: @ android_atomic_cmpxchg - ldmia sp!, {r4, lr} - 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 */ 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..f6cd8b0 100644 --- a/libcutils/atomic.c +++ b/libcutils/atomic.c @@ -14,326 +14,6 @@ * limitations under the License. */ -#include <cutils/atomic.h> -#ifdef HAVE_WIN32_THREADS -#include <windows.h> -#else -#include <sched.h> -#endif +#define inline -/*****************************************************************************/ -#if defined(HAVE_MACOSX_IPC) - -#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_inc(volatile int32_t* addr) { - return OSAtomicIncrement32Barrier((int32_t*)addr)-1; -} - -int32_t android_atomic_dec(volatile int32_t* addr) { - return OSAtomicDecrement32Barrier((int32_t*)addr)+1; -} - -int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { - return OSAtomicAdd32Barrier(value, (int32_t*)addr)-value; -} - -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; -} - -int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue|value, (int32_t*)addr) == 0); - 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)); - return oldValue; -} - -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { - 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; -} - -int64_t android_quasiatomic_read_64(volatile int64_t* addr) { - return OSAtomicAdd64Barrier(0, addr); -} - -#endif - - -/*****************************************************************************/ -#elif defined(__i386__) || defined(__x86_64__) - -void android_atomic_write(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_cmpxchg(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)); - return oldValue; -} - -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; -} - -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)); - return oldValue; -} - -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)); - return oldValue; -} - -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)); - return oldValue; -} - -int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) { - int xchg; - asm volatile - ( - " lock; cmpxchg %%ecx, (%%edx);" - " setne %%al;" - " andl $1, %%eax" - : "=a" (xchg) - : "a" (oldvalue), "c" (newvalue), "d" (addr) - ); - 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); - 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); - - pthread_mutex_lock(lock); - - if (*addr == oldvalue) { - *addr = newvalue; - result = 0; - } else { - result = 1; - } - pthread_mutex_unlock(lock); - 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; -} - -/*****************************************************************************/ -#elif __sh__ -// implementation for SuperH is in atomic-android-sh.c. - -#else - -#error "Unsupported atomic operations for this platform" - -#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 +#include <cutils/atomic-inline.h> diff --git a/libcutils/mspace.c b/libcutils/mspace.c index 63b199d..6d3b35c 100644 --- a/libcutils/mspace.c +++ b/libcutils/mspace.c @@ -271,4 +271,16 @@ size_t destroy_contiguous_mspace(mspace msp) { } return 0; } + +void *contiguous_mspace_sbrk0(mspace msp) { + struct mspace_contig_state *cs; + mstate ms; + const unsigned int pagesize = PAGESIZE; + + ms = (mstate)msp; + cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1)); + assert(cs->magic == CONTIG_STATE_MAGIC); + assert(cs->m == ms); + return cs->brk; +} #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/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index 3642647..bdc572e 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -71,7 +71,7 @@ int usage(void) -static unsigned char padding[2048] = { 0, }; +static unsigned char padding[4096] = { 0, }; int write_padding(int fd, unsigned pagesize, unsigned itemsize) { @@ -148,6 +148,12 @@ int main(int argc, char **argv) hdr.tags_addr = base + 0x00000100; } else if(!strcmp(arg, "--board")) { board = val; + } else if(!strcmp(arg,"--pagesize")) { + pagesize = strtoul(val, 0, 10); + if ((pagesize != 2048) && (pagesize != 4096)) { + fprintf(stderr,"error: unsupported page size %d\n", pagesize); + return -1; + } } else { return usage(); } diff --git a/patch.txt b/patch.txt new file mode 100644 index 0000000..258965d --- /dev/null +++ b/patch.txt @@ -0,0 +1,16 @@ +diff --git a/init/util.c b/init/util.c +index 4d98cc2..0667593 100755 +--- a/init/util.c ++++ b/init/util.c +@@ -657,8 +657,9 @@ static void get_hardware_name(void) + if (x) { + x += 2; + n = 0; +- while (*x && !isspace(*x)) { +- hardware[n++] = tolower(*x); ++ while (*x && *x != '\n') { ++ if (!isspace(*x)) ++ hardware[n++] = tolower(*x); + x++; + if (n == 31) break; + } diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 9a56bfd..380bb60 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..1e3e2d2 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -1,3 +1,5 @@ +on early-init + start ueventd on init @@ -14,7 +16,8 @@ loglevel 3 export ANDROID_DATA /data export EXTERNAL_STORAGE /mnt/sdcard export ASEC_MOUNTPOINT /mnt/asec - export BOOTCLASSPATH /system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar + export LOOP_MOUNTPOINT /mnt/obb + export BOOTCLASSPATH /system/framework/core.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/core-junit.jar # Backward compatibility symlink /system/etc /etc @@ -51,6 +54,10 @@ loglevel 3 mkdir /mnt/asec 0700 root system mount tmpfs tmpfs /mnt/asec mode=0755,gid=1000 + # Filesystem image public mount points. + mkdir /mnt/obb 0700 root system + mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000 + mount rootfs rootfs / ro remount write /proc/sys/kernel/panic_on_oops 1 @@ -80,13 +87,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 +120,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 +184,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 +275,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/sdcard/Android.mk b/sdcard/Android.mk new file mode 100644 index 0000000..c430ac8 --- /dev/null +++ b/sdcard/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= sdcard.c +LOCAL_MODULE:= sdcard + +LOCAL_SHARED_LIBRARIES := libc + +include $(BUILD_EXECUTABLE) diff --git a/sdcard/fuse.h b/sdcard/fuse.h new file mode 100644 index 0000000..3138da9 --- /dev/null +++ b/sdcard/fuse.h @@ -0,0 +1,578 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +/* + * from the libfuse FAQ (and consistent with the Linux Kernel license): + * + * Under what conditions may I distribute a filesystem that uses the + * raw kernel interface of FUSE? + * + * There are no restrictions whatsoever for using the raw kernel interface. + * + */ + +/* + * This file defines the kernel interface of FUSE + * + * Protocol changelog: + * + * 7.9: + * - new fuse_getattr_in input argument of GETATTR + * - add lk_flags in fuse_lk_in + * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in + * - add blksize field to fuse_attr + * - add file flags field to fuse_read_in and fuse_write_in + * + * 7.10 + * - add nonseekable open flag + * + * 7.11 + * - add IOCTL message + * - add unsolicited notification support + * - add POLL message and NOTIFY_POLL notification + * + * 7.12 + * - add umask flag to input argument of open, mknod and mkdir + * - add notification messages for invalidation of inodes and + * directory entries + * + * 7.13 + * - make max number of background requests and congestion threshold + * tunables + */ + +#ifndef _LINUX_FUSE_H +#define _LINUX_FUSE_H + +#include <linux/types.h> + +/* + * Version negotiation: + * + * Both the kernel and userspace send the version they support in the + * INIT request and reply respectively. + * + * If the major versions match then both shall use the smallest + * of the two minor versions for communication. + * + * If the kernel supports a larger major version, then userspace shall + * reply with the major version it supports, ignore the rest of the + * INIT message and expect a new INIT message from the kernel with a + * matching major version. + * + * If the library supports a larger major version, then it shall fall + * back to the major protocol version sent by the kernel for + * communication and reply with that major version (and an arbitrary + * supported minor version). + */ + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 13 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + __u64 ino; + __u64 size; + __u64 blocks; + __u64 atime; + __u64 mtime; + __u64 ctime; + __u32 atimensec; + __u32 mtimensec; + __u32 ctimensec; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 rdev; + __u32 blksize; + __u32 padding; +}; + +struct fuse_kstatfs { + __u64 blocks; + __u64 bfree; + __u64 bavail; + __u64 files; + __u64 ffree; + __u32 bsize; + __u32 namelen; + __u32 frsize; + __u32 padding; + __u32 spare[6]; +}; + +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) +#define FATTR_ATIME_NOW (1 << 7) +#define FATTR_MTIME_NOW (1 << 8) +#define FATTR_LOCKOWNER (1 << 9) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + * FOPEN_NONSEEKABLE: the file is not seekable + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) +#define FOPEN_NONSEEKABLE (1 << 2) + +/** + * INIT request/reply flags + * + * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." + * FUSE_DONT_MASK: don't apply umask to file mode on create operations + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) +#define FUSE_FILE_OPS (1 << 2) +#define FUSE_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_EXPORT_SUPPORT (1 << 4) +#define FUSE_BIG_WRITES (1 << 5) +#define FUSE_DONT_MASK (1 << 6) + +/** + * CUSE INIT request/reply flags + * + * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl + */ +#define CUSE_UNRESTRICTED_IOCTL (1 << 0) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) + +/** + * Getattr flags + */ +#define FUSE_GETATTR_FH (1 << 0) + +/** + * Lock flags + */ +#define FUSE_LK_FLOCK (1 << 0) + +/** + * WRITE flags + * + * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed + * FUSE_WRITE_LOCKOWNER: lock_owner field is valid + */ +#define FUSE_WRITE_CACHE (1 << 0) +#define FUSE_WRITE_LOCKOWNER (1 << 1) + +/** + * Read flags + */ +#define FUSE_READ_LOCKOWNER (1 << 1) + +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * + * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) + +#define FUSE_IOCTL_MAX_IOV 256 + +/** + * Poll flags + * + * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify + */ +#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, + FUSE_IOCTL = 39, + FUSE_POLL = 40, + + /* CUSE specific operations */ + CUSE_INIT = 4096, +}; + +enum fuse_notify_code { + FUSE_NOTIFY_POLL = 1, + FUSE_NOTIFY_INVAL_INODE = 2, + FUSE_NOTIFY_INVAL_ENTRY = 3, + FUSE_NOTIFY_CODE_MAX, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 + +struct fuse_entry_out { + __u64 nodeid; /* Inode ID */ + __u64 generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + __u64 entry_valid; /* Cache timeout for the name */ + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 entry_valid_nsec; + __u32 attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + __u64 nlookup; +}; + +struct fuse_getattr_in { + __u32 getattr_flags; + __u32 dummy; + __u64 fh; +}; + +#define FUSE_COMPAT_ATTR_OUT_SIZE 96 + +struct fuse_attr_out { + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 attr_valid_nsec; + __u32 dummy; + struct fuse_attr attr; +}; + +#define FUSE_COMPAT_MKNOD_IN_SIZE 8 + +struct fuse_mknod_in { + __u32 mode; + __u32 rdev; + __u32 umask; + __u32 padding; +}; + +struct fuse_mkdir_in { + __u32 mode; + __u32 umask; +}; + +struct fuse_rename_in { + __u64 newdir; +}; + +struct fuse_link_in { + __u64 oldnodeid; +}; + +struct fuse_setattr_in { + __u32 valid; + __u32 padding; + __u64 fh; + __u64 size; + __u64 lock_owner; + __u64 atime; + __u64 mtime; + __u64 unused2; + __u32 atimensec; + __u32 mtimensec; + __u32 unused3; + __u32 mode; + __u32 unused4; + __u32 uid; + __u32 gid; + __u32 unused5; +}; + +struct fuse_open_in { + __u32 flags; + __u32 unused; +}; + +struct fuse_create_in { + __u32 flags; + __u32 mode; + __u32 umask; + __u32 padding; +}; + +struct fuse_open_out { + __u64 fh; + __u32 open_flags; + __u32 padding; +}; + +struct fuse_release_in { + __u64 fh; + __u32 flags; + __u32 release_flags; + __u64 lock_owner; +}; + +struct fuse_flush_in { + __u64 fh; + __u32 unused; + __u32 padding; + __u64 lock_owner; +}; + +struct fuse_read_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 read_flags; + __u64 lock_owner; + __u32 flags; + __u32 padding; +}; + +#define FUSE_COMPAT_WRITE_IN_SIZE 24 + +struct fuse_write_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 write_flags; + __u64 lock_owner; + __u32 flags; + __u32 padding; +}; + +struct fuse_write_out { + __u32 size; + __u32 padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + __u64 fh; + __u32 fsync_flags; + __u32 padding; +}; + +struct fuse_setxattr_in { + __u32 size; + __u32 flags; +}; + +struct fuse_getxattr_in { + __u32 size; + __u32 padding; +}; + +struct fuse_getxattr_out { + __u32 size; + __u32 padding; +}; + +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; + __u32 lk_flags; + __u32 padding; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + __u32 mask; + __u32 padding; +}; + +struct fuse_init_in { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; +}; + +struct fuse_init_out { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; + __u16 max_background; + __u16 congestion_threshold; + __u32 max_write; +}; + +#define CUSE_INIT_INFO_MAX 4096 + +struct cuse_init_in { + __u32 major; + __u32 minor; + __u32 unused; + __u32 flags; +}; + +struct cuse_init_out { + __u32 major; + __u32 minor; + __u32 unused; + __u32 flags; + __u32 max_read; + __u32 max_write; + __u32 dev_major; /* chardev major */ + __u32 dev_minor; /* chardev minor */ + __u32 spare[10]; +}; + +struct fuse_interrupt_in { + __u64 unique; +}; + +struct fuse_bmap_in { + __u64 block; + __u32 blocksize; + __u32 padding; +}; + +struct fuse_bmap_out { + __u64 block; +}; + +struct fuse_ioctl_in { + __u64 fh; + __u32 flags; + __u32 cmd; + __u64 arg; + __u32 in_size; + __u32 out_size; +}; + +struct fuse_ioctl_out { + __s32 result; + __u32 flags; + __u32 in_iovs; + __u32 out_iovs; +}; + +struct fuse_poll_in { + __u64 fh; + __u64 kh; + __u32 flags; + __u32 padding; +}; + +struct fuse_poll_out { + __u32 revents; + __u32 padding; +}; + +struct fuse_notify_poll_wakeup_out { + __u64 kh; +}; + +struct fuse_in_header { + __u32 len; + __u32 opcode; + __u64 unique; + __u64 nodeid; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 padding; +}; + +struct fuse_out_header { + __u32 len; + __s32 error; + __u64 unique; +}; + +struct fuse_dirent { + __u64 ino; + __u64 off; + __u32 namelen; + __u32 type; + char name[0]; +}; + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) + +struct fuse_notify_inval_inode_out { + __u64 ino; + __s64 off; + __s64 len; +}; + +struct fuse_notify_inval_entry_out { + __u64 parent; + __u32 namelen; + __u32 padding; +}; + +#endif /* _LINUX_FUSE_H */ diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c new file mode 100644 index 0000000..82e6354 --- /dev/null +++ b/sdcard/sdcard.c @@ -0,0 +1,795 @@ +/* + * 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 <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <sys/uio.h> +#include <dirent.h> + +#include <private/android_filesystem_config.h> + +#include "fuse.h" + +/* README + * + * What is this? + * + * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style + * directory permissions (all files are given fixed owner, group, and + * permissions at creation, owner, group, and permissions are not + * changeable, symlinks and hardlinks are not createable, etc. + * + * usage: sdcard <path> <uid> <gid> + * + * It must be run as root, but will change to uid/gid as soon as it + * mounts a filesystem on /mnt/sdcard. It will refuse to run if uid or + * gid are zero. + * + * + * Things I believe to be true: + * + * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK, + * CREAT) must bump that node's refcount + * - don't forget that FORGET can forget multiple references (req->nlookup) + * - if an op that returns a fuse_entry fails writing the reply to the + * kernel, you must rollback the refcount to reflect the reference the + * kernel did not actually acquire + * + * + * Bugs: + * + * - need to move/rename node on RENAME + */ + +#define FUSE_TRACE 0 + +#if FUSE_TRACE +#define TRACE(x...) fprintf(stderr,x) +#else +#define TRACE(x...) do {} while (0) +#endif + +#define ERROR(x...) fprintf(stderr,x) + +#define FUSE_UNKNOWN_INO 0xffffffff + +#define MOUNT_POINT "/mnt/sdcard" + +struct handle { + struct node *node; + int fd; +}; + +struct dirhandle { + struct node *node; + DIR *d; +}; + +struct node { + __u64 nid; + __u64 gen; + + struct node *next; + struct node *child; + struct node *all; + struct node *parent; + + __u32 refcount; + __u32 namelen; + + char name[1]; +}; + +struct fuse { + __u64 next_generation; + __u64 next_node_id; + + int fd; + + struct node *all; + + struct node root; + char rootpath[1024]; +}; + +#define PATH_BUFFER_SIZE 1024 + +char *node_get_path(struct node *node, char *buf, const char *name) +{ + char *out = buf + PATH_BUFFER_SIZE - 1; + int len; + out[0] = 0; + + if (name) { + len = strlen(name); + goto start; + } + + while (node) { + name = node->name; + len = node->namelen; + node = node->parent; + start: + if ((len + 1) > (out - buf)) + return 0; + out -= len; + memcpy(out, name, len); + out --; + out[0] = '/'; + } + + return out; +} + +void attr_from_stat(struct fuse_attr *attr, struct stat *s) +{ + attr->ino = s->st_ino; + attr->size = s->st_size; + attr->blocks = s->st_blocks; + attr->atime = s->st_atime; + attr->mtime = s->st_mtime; + attr->ctime = s->st_ctime; + attr->atimensec = s->st_atime_nsec; + attr->mtimensec = s->st_mtime_nsec; + attr->ctimensec = s->st_ctime_nsec; + attr->mode = s->st_mode; + attr->nlink = s->st_nlink; + + /* force permissions to something reasonable: + * world readable + * writable by the sdcard group + */ + if (attr->mode & 0100) { + attr->mode = (attr->mode & (~0777)) | 0775; + } else { + attr->mode = (attr->mode & (~0777)) | 0664; + } + + /* all files owned by root.sdcard */ + attr->uid = 0; + attr->gid = AID_SDCARD_RW; +} + +int node_get_attr(struct node *node, struct fuse_attr *attr) +{ + int res; + struct stat s; + char *path, buffer[PATH_BUFFER_SIZE]; + + path = node_get_path(node, buffer, 0); + res = lstat(path, &s); + if (res < 0) { + ERROR("lstat('%s') errno %d\n", path, errno); + return -1; + } + + attr_from_stat(attr, &s); + attr->ino = node->nid; + + return 0; +} + +struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen) +{ + struct node *node; + int namelen = strlen(name); + + node = calloc(1, sizeof(struct node) + namelen); + if (node == 0) { + return 0; + } + + node->nid = nid; + node->gen = gen; + node->parent = parent; + node->next = parent->child; + parent->child = node; + memcpy(node->name, name, namelen + 1); + node->namelen = namelen; + parent->refcount++; + + return node; +} + +void fuse_init(struct fuse *fuse, int fd, const char *path) +{ + fuse->fd = fd; + fuse->next_node_id = 2; + fuse->next_generation = 0; + + fuse->all = &fuse->root; + + fuse->root.nid = FUSE_ROOT_ID; /* 1 */ + fuse->root.next = 0; + fuse->root.child = 0; + fuse->root.parent = 0; + + fuse->root.all = 0; + fuse->root.refcount = 2; + + strcpy(fuse->root.name, path); + fuse->root.namelen = strlen(fuse->root.name); +} + +static inline void *id_to_ptr(__u64 nid) +{ + return (void *) nid; +} + +static inline __u64 ptr_to_id(void *ptr) +{ + return (__u64) ptr; +} + + +struct node *lookup_by_inode(struct fuse *fuse, __u64 nid) +{ + if (nid == FUSE_ROOT_ID) { + return &fuse->root; + } else { + return id_to_ptr(nid); + } +} + +struct node *lookup_child_by_name(struct node *node, const char *name) +{ + for (node = node->child; node; node = node->next) { + if (!strcmp(name, node->name)) { + return node; + } + } + return 0; +} + +struct node *lookup_child_by_inode(struct node *node, __u64 nid) +{ + for (node = node->child; node; node = node->next) { + if (node->nid == nid) { + return node; + } + } + return 0; +} + +struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name, + struct fuse_attr *attr) +{ + int res; + struct stat s; + char *path, buffer[PATH_BUFFER_SIZE]; + struct node *node; + + path = node_get_path(parent, buffer, name); + /* XXX error? */ + + res = lstat(path, &s); + if (res < 0) + return 0; + + node = lookup_child_by_name(parent, name); + if (!node) { + node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++); + if (!node) + return 0; + node->nid = ptr_to_id(node); + node->all = fuse->all; + fuse->all = node; + } + + attr_from_stat(attr, &s); + attr->ino = node->nid; + + return node; +} + +void node_release(struct node *node) +{ + TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); + node->refcount--; + if (node->refcount == 0) { + if (node->parent->child == node) { + node->parent->child = node->parent->child->next; + } else { + struct node *node2; + + node2 = node->parent->child; + while (node2->next != node) + node2 = node2->next; + node2->next = node->next; + } + + TRACE("DESTROY %p (%s)\n", node, node->name); + + node_release(node->parent); + + node->parent = 0; + node->next = 0; + + /* TODO: remove debugging - poison memory */ + memset(node, 0xef, sizeof(*node) + strlen(node->name)); + + free(node); + } +} + +void fuse_status(struct fuse *fuse, __u64 unique, int err) +{ + struct fuse_out_header hdr; + hdr.len = sizeof(hdr); + hdr.error = err; + hdr.unique = unique; + if (err) { +// ERROR("*** %d ***\n", err); + } + write(fuse->fd, &hdr, sizeof(hdr)); +} + +void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) +{ + struct fuse_out_header hdr; + struct iovec vec[2]; + int res; + + hdr.len = len + sizeof(hdr); + hdr.error = 0; + hdr.unique = unique; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = data; + vec[1].iov_len = len; + + res = writev(fuse->fd, vec, 2); + if (res < 0) { + ERROR("*** REPLY FAILED *** %d\n", errno); + } +} + +void lookup_entry(struct fuse *fuse, struct node *node, + const char *name, __u64 unique) +{ + struct fuse_entry_out out; + + memset(&out, 0, sizeof(out)); + + node = node_lookup(fuse, node, name, &out.attr); + if (!node) { + fuse_status(fuse, unique, -ENOENT); + return; + } + + node->refcount++; +// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); + out.nodeid = node->nid; + out.generation = node->gen; + out.entry_valid = 10; + out.attr_valid = 10; + + fuse_reply(fuse, unique, &out, sizeof(out)); +} + +void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len) +{ + struct node *node; + + if ((len < sizeof(*hdr)) || (hdr->len != len)) { + ERROR("malformed header\n"); + return; + } + + len -= hdr->len; + + if (hdr->nodeid) { + node = lookup_by_inode(fuse, hdr->nodeid); + if (!node) { + fuse_status(fuse, hdr->unique, -ENOENT); + return; + } + } else { + node = 0; + } + + switch (hdr->opcode) { + case FUSE_LOOKUP: { /* bytez[] -> entry_out */ + TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data); + lookup_entry(fuse, node, (char*) data, hdr->unique); + return; + } + case FUSE_FORGET: { + struct fuse_forget_in *req = data; + TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup); + /* no reply */ + while (req->nlookup--) + node_release(node); + return; + } + case FUSE_GETATTR: { /* getattr_in -> attr_out */ + struct fuse_getattr_in *req = data; + struct fuse_attr_out out; + + TRACE("GETATTR flags=%x fh=%llx\n",req->getattr_flags, req->fh); + + memset(&out, 0, sizeof(out)); + node_get_attr(node, &out.attr); + out.attr_valid = 10; + + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return; + } + case FUSE_SETATTR: { /* setattr_in -> attr_out */ + struct fuse_setattr_in *req = data; + struct fuse_attr_out out; + TRACE("SETATTR fh=%llx id=%llx valid=%x\n", + req->fh, hdr->nodeid, req->valid); + + /* XXX */ + + memset(&out, 0, sizeof(out)); + node_get_attr(node, &out.attr); + out.attr_valid = 10; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return; + } +// case FUSE_READLINK: +// case FUSE_SYMLINK: + case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */ + struct fuse_mknod_in *req = data; + char *path, buffer[PATH_BUFFER_SIZE]; + char *name = ((char*) data) + sizeof(*req); + int res; + TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid); + path = node_get_path(node, buffer, name); + + req->mode = (req->mode & (~0777)) | 0664; + res = mknod(path, req->mode, req->rdev); /* XXX perm?*/ + if (res < 0) { + fuse_status(fuse, hdr->unique, -errno); + } else { + lookup_entry(fuse, node, name, hdr->unique); + } + return; + } + case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */ + struct fuse_mkdir_in *req = data; + struct fuse_entry_out out; + char *path, buffer[PATH_BUFFER_SIZE]; + char *name = ((char*) data) + sizeof(*req); + int res; + TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode); + path = node_get_path(node, buffer, name); + + req->mode = (req->mode & (~0777)) | 0775; + res = mkdir(path, req->mode); + if (res < 0) { + fuse_status(fuse, hdr->unique, -errno); + } else { + lookup_entry(fuse, node, name, hdr->unique); + } + return; + } + case FUSE_UNLINK: { /* bytez[] -> */ + char *path, buffer[PATH_BUFFER_SIZE]; + int res; + TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid); + path = node_get_path(node, buffer, (char*) data); + res = unlink(path); + fuse_status(fuse, hdr->unique, res ? -errno : 0); + return; + } + case FUSE_RMDIR: { /* bytez[] -> */ + char *path, buffer[PATH_BUFFER_SIZE]; + int res; + TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid); + path = node_get_path(node, buffer, (char*) data); + res = rmdir(path); + fuse_status(fuse, hdr->unique, res ? -errno : 0); + return; + } + case FUSE_RENAME: { /* rename_in, oldname, newname -> */ + struct fuse_rename_in *req = data; + char *oldname = ((char*) data) + sizeof(*req); + char *newname = oldname + strlen(oldname) + 1; + char *oldpath, oldbuffer[PATH_BUFFER_SIZE]; + char *newpath, newbuffer[PATH_BUFFER_SIZE]; + struct node *newnode; + int res; + + newnode = lookup_by_inode(fuse, req->newdir); + if (!newnode) { + fuse_status(fuse, hdr->unique, -ENOENT); + return; + } + + oldpath = node_get_path(node, oldbuffer, oldname); + newpath = node_get_path(newnode, newbuffer, newname); + + res = rename(oldpath, newpath); + fuse_status(fuse, hdr->unique, res ? -errno : 0); + return; + } +// case FUSE_LINK: + case FUSE_OPEN: { /* open_in -> open_out */ + struct fuse_open_in *req = data; + struct fuse_open_out out; + char *path, buffer[PATH_BUFFER_SIZE]; + struct handle *h; + + h = malloc(sizeof(*h)); + if (!h) { + fuse_status(fuse, hdr->unique, -ENOMEM); + return; + } + + path = node_get_path(node, buffer, 0); + TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h); + h->fd = open(path, req->flags); + if (h->fd < 0) { + ERROR("ERROR\n"); + fuse_status(fuse, hdr->unique, errno); + free(h); + return; + } + out.fh = ptr_to_id(h); + out.open_flags = 0; + out.padding = 0; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return; + } + case FUSE_READ: { /* read_in -> byte[] */ + char buffer[128 * 1024]; + struct fuse_read_in *req = data; + struct handle *h = id_to_ptr(req->fh); + int res; + TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); + if (req->size > sizeof(buffer)) { + fuse_status(fuse, hdr->unique, -EINVAL); + return; + } + res = pread(h->fd, buffer, req->size, req->offset); + if (res < 0) { + fuse_status(fuse, hdr->unique, errno); + return; + } + fuse_reply(fuse, hdr->unique, buffer, res); + return; + } + case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */ + struct fuse_write_in *req = data; + struct fuse_write_out out; + struct handle *h = id_to_ptr(req->fh); + int res; + TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); + res = pwrite(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset); + if (res < 0) { + fuse_status(fuse, hdr->unique, errno); + return; + } + out.size = res; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + goto oops; + } + case FUSE_STATFS: { /* getattr_in -> attr_out */ + struct statfs stat; + struct fuse_statfs_out out; + int res; + + TRACE("STATFS\n"); + + if (statfs(fuse->root.name, &stat)) { + fuse_status(fuse, hdr->unique, -errno); + return; + } + + memset(&out, 0, sizeof(out)); + out.st.blocks = stat.f_blocks; + out.st.bfree = stat.f_bfree; + out.st.bavail = stat.f_bavail; + out.st.files = stat.f_files; + out.st.ffree = stat.f_ffree; + out.st.bsize = stat.f_bsize; + out.st.namelen = stat.f_namelen; + out.st.frsize = stat.f_frsize; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return; + } + case FUSE_RELEASE: { /* release_in -> */ + struct fuse_release_in *req = data; + struct handle *h = id_to_ptr(req->fh); + TRACE("RELEASE %p(%d)\n", h, h->fd); + close(h->fd); + free(h); + fuse_status(fuse, hdr->unique, 0); + return; + } +// case FUSE_FSYNC: +// case FUSE_SETXATTR: +// case FUSE_GETXATTR: +// case FUSE_LISTXATTR: +// case FUSE_REMOVEXATTR: + case FUSE_FLUSH: + fuse_status(fuse, hdr->unique, 0); + return; + case FUSE_OPENDIR: { /* open_in -> open_out */ + struct fuse_open_in *req = data; + struct fuse_open_out out; + char *path, buffer[PATH_BUFFER_SIZE]; + struct dirhandle *h; + + h = malloc(sizeof(*h)); + if (!h) { + fuse_status(fuse, hdr->unique, -ENOMEM); + return; + } + + path = node_get_path(node, buffer, 0); + TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path); + h->d = opendir(path); + if (h->d == 0) { + ERROR("ERROR\n"); + fuse_status(fuse, hdr->unique, -errno); + free(h); + return; + } + out.fh = ptr_to_id(h); + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return; + } + case FUSE_READDIR: { + struct fuse_read_in *req = data; + char buffer[8192]; + struct fuse_dirent *fde = (struct fuse_dirent*) buffer; + struct dirent *de; + struct dirhandle *h = id_to_ptr(req->fh); + TRACE("READDIR %p\n", h); + de = readdir(h->d); + if (!de) { + fuse_status(fuse, hdr->unique, 0); + return; + } + fde->ino = FUSE_UNKNOWN_INO; + fde->off = 0; + fde->type = de->d_type; + fde->namelen = strlen(de->d_name); + memcpy(fde->name, de->d_name, fde->namelen + 1); + fuse_reply(fuse, hdr->unique, fde, + FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); + return; + } + case FUSE_RELEASEDIR: { /* release_in -> */ + struct fuse_release_in *req = data; + struct dirhandle *h = id_to_ptr(req->fh); + TRACE("RELEASEDIR %p\n",h); + closedir(h->d); + free(h); + fuse_status(fuse, hdr->unique, 0); + return; + } +// case FUSE_FSYNCDIR: + case FUSE_INIT: { /* init_in -> init_out */ + struct fuse_init_in *req = data; + struct fuse_init_out out; + + TRACE("INIT ver=%d.%d maxread=%d flags=%x\n", + req->major, req->minor, req->max_readahead, req->flags); + + out.major = FUSE_KERNEL_VERSION; + out.minor = FUSE_KERNEL_MINOR_VERSION; + out.max_readahead = req->max_readahead; + out.flags = 0; + out.max_background = 32; + out.congestion_threshold = 32; + out.max_write = 256 * 1024; + + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return; + } + default: { + struct fuse_out_header h; + ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n", + hdr->opcode, hdr->unique, hdr->nodeid); + + oops: + h.len = sizeof(h); + h.error = -ENOSYS; + h.unique = hdr->unique; + write(fuse->fd, &h, sizeof(h)); + break; + } + } +} + +void handle_fuse_requests(struct fuse *fuse) +{ + unsigned char req[256 * 1024 + 128]; + int len; + + for (;;) { + len = read(fuse->fd, req, 8192); + if (len < 0) { + if (errno == EINTR) + continue; + ERROR("handle_fuse_requests: errno=%d\n", errno); + return; + } + handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len); + } +} + +int main(int argc, char **argv) +{ + struct fuse fuse; + char opts[256]; + int fd; + int res; + unsigned uid; + unsigned gid; + const char *path; + + if (argc != 4) { + ERROR("usage: sdcard <path> <uid> <gid>\n"); + return -1; + } + + uid = strtoul(argv[2], 0, 10); + gid = strtoul(argv[3], 0, 10); + if (!uid || !gid) { + ERROR("uid and gid must be nonzero\n"); + return -1; + } + + path = argv[1]; + + /* cleanup from previous instance, if necessary */ + umount2(MOUNT_POINT, 2); + + fd = open("/dev/fuse", O_RDWR); + if (fd < 0){ + ERROR("cannot open fuse device (%d)\n", errno); + return -1; + } + + sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other," + "user_id=%d,group_id=%d", fd, uid, gid); + + res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts); + if (res < 0) { + ERROR("cannot mount fuse filesystem (%d)\n", errno); + return -1; + } + + if (setgid(gid) < 0) { + ERROR("cannot setgid!\n"); + return -1; + } + if (setuid(uid) < 0) { + ERROR("cannot setuid!\n"); + return -1; + } + + fuse_init(&fuse, fd, path); + + umask(0); + handle_fuse_requests(&fuse); + + return 0; +} diff --git a/sh/ThirdPartyProject.prop b/sh/ThirdPartyProject.prop new file mode 100644 index 0000000..eb9167e --- /dev/null +++ b/sh/ThirdPartyProject.prop @@ -0,0 +1,9 @@ +# Copyright 2010 Google Inc. All Rights Reserved. +#Fri Jul 16 10:03:08 PDT 2010 +currentVersion=Unknown +version=1.17 +isNative=true +name=ash +keywords=ash +onDevice=true +homepage=http\://www.in-ulm.de/~mascheck/various/ash/ diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 58937cf..ef3980a 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -53,7 +53,8 @@ TOOLS := \ uptime \ vmstat \ nandread \ - ionice + ionice \ + lsof LOCAL_SRC_FILES:= \ toolbox.c \ diff --git a/toolbox/NOTICE b/toolbox/NOTICE index 12f28b9..895b49a 100644 --- a/toolbox/NOTICE +++ b/toolbox/NOTICE @@ -1,5 +1,67 @@ -Copyright (c) 2008, The Android Open Source Project +Copyright (c) 2010, The Android Open Source Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Android Open Source Project nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 2009, The Android Open Source Project. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Android Open Source Project nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +Copyright (c) 2008, The Android Open Source Project. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/toolbox/ls.c b/toolbox/ls.c index 6c8de7a..962bf47 100644 --- a/toolbox/ls.c +++ b/toolbox/ls.c @@ -297,8 +297,8 @@ static int listfile_long(const char *path, int flags) date, name); break; case S_IFREG: - printf("%s %-8s %-8s %8d %s %s\n", - mode, user, group, (int) s.st_size, date, name); + printf("%s %-8s %-8s %8lld %s %s\n", + mode, user, group, s.st_size, date, name); break; case S_IFLNK: { char linkto[256]; diff --git a/toolbox/lsof.c b/toolbox/lsof.c new file mode 100644 index 0000000..99891db --- /dev/null +++ b/toolbox/lsof.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2010, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define BUF_MAX 1024 +#define CMD_DISPLAY_MAX 10 + +struct pid_info_t { + pid_t pid; + + char cmdline[CMD_DISPLAY_MAX]; + + char path[PATH_MAX]; + ssize_t parent_length; +}; + +void print_header() +{ + printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n", + "COMMAND", + "PID", + "USER", + "FD", + "TYPE", + "DEVICE", + "SIZE/OFF", + "NODE", + "NAME"); +} + +void print_type(char *type, struct pid_info_t* info) +{ + static ssize_t link_dest_size; + static char link_dest[PATH_MAX]; + + strncat(info->path, type, sizeof(info->path)); + if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) { + if (errno == ENOENT) + goto out; + + snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno)); + } else { + link_dest[link_dest_size] = '\0'; + } + + // Things that are just the root filesystem are uninteresting (we already know) + if (!strcmp(link_dest, "/")) + goto out; + + printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", info->cmdline, info->pid, "???", type, + "???", "???", "???", "???", link_dest); + +out: + info->path[info->parent_length] = '\0'; +} + +// Prints out all file that have been memory mapped +void print_maps(struct pid_info_t* info) +{ + FILE *maps; + char buffer[PATH_MAX + 100]; + + size_t offset; + int major, minor; + char device[10]; + long int inode; + char file[PATH_MAX]; + + strncat(info->path, "maps", sizeof(info->path)); + + maps = fopen(info->path, "r"); + if (!maps) + goto out; + + while (fscanf(maps, "%*x-%*x %*s %zx %5s %ld %s\n", &offset, device, &inode, + file) == 4) { + // We don't care about non-file maps + if (inode == 0 || !strcmp(device, "00:00")) + continue; + + printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n", info->cmdline, info->pid, "???", "mem", + "???", device, offset, inode, file); + } + + fclose(maps); + +out: + info->path[info->parent_length] = '\0'; +} + +// Prints out all open file descriptors +void print_fds(struct pid_info_t* info) +{ + static char* fd_path = "fd/"; + strncat(info->path, fd_path, sizeof(info->path)); + + int previous_length = info->parent_length; + info->parent_length += strlen(fd_path); + + DIR *dir = opendir(info->path); + if (dir == NULL) { + char msg[BUF_MAX]; + snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno)); + printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", info->cmdline, info->pid, "???", "FDS", + "", "", "", "", msg); + goto out; + } + + struct dirent* de; + while ((de = readdir(dir))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + print_type(de->d_name, info); + } + closedir(dir); + +out: + info->parent_length = previous_length; + info->path[info->parent_length] = '\0'; +} + +void lsof_dumpinfo(pid_t pid) +{ + int fd; + struct pid_info_t info; + info.pid = pid; + + snprintf(info.path, sizeof(info.path), "/proc/%d/", pid); + + info.parent_length = strlen(info.path); + + // Read the command line information; each argument is terminated with NULL. + strncat(info.path, "cmdline", sizeof(info.path)); + fd = open(info.path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Couldn't read %s\n", info.path); + return; + } + char cmdline[PATH_MAX]; + if (read(fd, cmdline, sizeof(cmdline)) < 0) { + fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno)); + close(fd); + return; + } + close(fd); + info.path[info.parent_length] = '\0'; + + // We only want the basename of the cmdline + strncpy(info.cmdline, basename(cmdline), sizeof(info.cmdline)); + info.cmdline[sizeof(info.cmdline)-1] = '\0'; + + // Read each of these symlinks + print_type("cwd", &info); + print_type("exe", &info); + print_type("root", &info); + + print_fds(&info); + print_maps(&info); +} + +int lsof_main(int argc, char *argv[]) +{ + DIR *dir = opendir("/proc"); + if (dir == NULL) { + fprintf(stderr, "Couldn't open /proc\n"); + return -1; + } + + print_header(); + + struct dirent* de; + while ((de = readdir(dir))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + // Only inspect directories that are PID numbers + char* endptr; + long int pid = strtol(de->d_name, &endptr, 10); + if (*endptr != '\0') + continue; + + lsof_dumpinfo(pid); + } + closedir(dir); + + return 0; +} diff --git a/toolbox/uptime.c b/toolbox/uptime.c index 8b1983d..3d8061c 100644 --- a/toolbox/uptime.c +++ b/toolbox/uptime.c @@ -1,17 +1,32 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2010, The Android Open Source Project + * All rights reserved. * - * 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 + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include <sys/time.h> |
