diff options
250 files changed, 12693 insertions, 11882 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 7744d2b..1a25106 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -95,16 +95,6 @@ endif # adbd device daemon # ========================================================= -BUILD_ADBD := true - -# build adbd for the Linux simulator build -# so we can use it to test the adb USB gadget driver on x86 -#ifeq ($(HOST_OS),linux) -# BUILD_ADBD := true -#endif - - -ifeq ($(BUILD_ADBD),true) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -127,10 +117,8 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -# TODO: This should probably be board specific, whether or not the kernel has -# the gadget driver; rather than relying on the architecture type. -ifeq ($(TARGET_ARCH),arm) -LOCAL_CFLAGS += -DANDROID_GADGET=1 +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1 endif LOCAL_MODULE := adbd @@ -142,8 +130,6 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) LOCAL_STATIC_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) -endif - # adb host tool for device-as-host # ========================================================= @@ -131,6 +131,58 @@ void adb_trace_init(void) } } +#if !ADB_HOST +/* + * Implements ADB tracing inside the emulator. + */ + +#include <stdarg.h> + +/* + * Redefine open and write for qemu_pipe.h that contains inlined references + * to those routines. We will redifine them back after qemu_pipe.h inclusion. + */ + +#undef open +#undef write +#define open adb_open +#define write adb_write +#include <hardware/qemu_pipe.h> +#undef open +#undef write +#define open ___xxx_open +#define write ___xxx_write + +/* A handle to adb-debug qemud service in the emulator. */ +int adb_debug_qemu = -1; + +/* Initializes connection with the adb-debug qemud service in the emulator. */ +static int adb_qemu_trace_init(void) +{ + char con_name[32]; + + if (adb_debug_qemu >= 0) { + return 0; + } + + /* adb debugging QEMUD service connection request. */ + snprintf(con_name, sizeof(con_name), "qemud:adb-debug"); + adb_debug_qemu = qemu_pipe_open(con_name); + return (adb_debug_qemu >= 0) ? 0 : -1; +} + +void adb_qemu_trace(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + char msg[1024]; + + if (adb_debug_qemu >= 0) { + vsnprintf(msg, sizeof(msg), fmt, args); + adb_write(adb_debug_qemu, msg, strlen(msg)); + } +} +#endif /* !ADB_HOST */ apacket *get_apacket(void) { @@ -299,6 +351,13 @@ void parse_banner(char *banner, atransport *t) return; } + if(!strcmp(type, "sideload")) { + D("setting connection_state to CS_SIDELOAD\n"); + t->connection_state = CS_SIDELOAD; + update_transports(); + return; + } + t->connection_state = CS_HOST; } @@ -835,12 +894,47 @@ void build_local_name(char* target_str, size_t target_size, int server_port) snprintf(target_str, target_size, "tcp:%d", server_port); } +#if !ADB_HOST +static int should_drop_privileges() { +#ifndef ALLOW_ADBD_ROOT + return 1; +#else /* ALLOW_ADBD_ROOT */ + int secure = 0; + char value[PROPERTY_VALUE_MAX]; + + /* run adbd in secure mode if ro.secure is set and + ** we are not in the emulator + */ + property_get("ro.kernel.qemu", value, ""); + if (strcmp(value, "1") != 0) { + property_get("ro.secure", value, "1"); + if (strcmp(value, "1") == 0) { + // don't run as root if ro.secure is set... + secure = 1; + + // ... except we allow running as root in userdebug builds if the + // service.adb.root property has been set by the "adb root" command + property_get("ro.debuggable", value, ""); + if (strcmp(value, "1") == 0) { + property_get("service.adb.root", value, ""); + if (strcmp(value, "1") == 0) { + secure = 0; + } + } + } + } + return secure; +#endif /* ALLOW_ADBD_ROOT */ +} +#endif /* !ADB_HOST */ + int adb_main(int is_daemon, int server_port) { #if !ADB_HOST - int secure = 0; int port; char value[PROPERTY_VALUE_MAX]; + + umask(000); #endif atexit(adb_cleanup); @@ -866,31 +960,10 @@ int adb_main(int is_daemon, int server_port) exit(1); } #else - /* run adbd in secure mode if ro.secure is set and - ** we are not in the emulator - */ - property_get("ro.kernel.qemu", value, ""); - if (strcmp(value, "1") != 0) { - property_get("ro.secure", value, "1"); - if (strcmp(value, "1") == 0) { - // don't run as root if ro.secure is set... - secure = 1; - - // ... except we allow running as root in userdebug builds if the - // service.adb.root property has been set by the "adb root" command - property_get("ro.debuggable", value, ""); - if (strcmp(value, "1") == 0) { - property_get("service.adb.root", value, ""); - if (strcmp(value, "1") == 0) { - secure = 0; - } - } - } - } /* don't listen on a port (default 5037) if running in secure mode */ /* don't run as root if we are running in secure mode */ - if (secure) { + if (should_drop_privileges()) { struct __user_cap_header_struct header; struct __user_cap_data_struct cap; @@ -905,13 +978,14 @@ int adb_main(int is_daemon, int server_port) ** AID_INET to diagnose network issues (netcfg, ping) ** AID_GRAPHICS to access the frame buffer ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump) + ** AID_SDCARD_R to allow reading from the SD card ** AID_SDCARD_RW to allow writing to the SD card ** AID_MOUNT to allow unmounting the SD card before rebooting ** AID_NET_BW_STATS to read out qtaguid statistics */ gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS, - AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_RW, AID_MOUNT, - AID_NET_BW_STATS }; + AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW, + AID_MOUNT, AID_NET_BW_STATS }; if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) { exit(1); } @@ -1278,6 +1352,9 @@ int main(int argc, char **argv) D("Handling commandline()\n"); return adb_commandline(argc - 1, argv + 1); #else + /* If adbd runs inside the emulator this will enable adb tracing via + * adb-debug qemud service in the emulator. */ + adb_qemu_trace_init(); if((argc > 1) && (!strcmp(argv[1],"recovery"))) { adb_device_banner = "recovery"; recovery_mode = 1; @@ -86,6 +86,11 @@ struct asocket { */ int closing; + /* flag: quit adbd when both ends close the + ** local service socket + */ + int exit_on_close; + /* the asocket we are connected to */ @@ -345,6 +350,21 @@ typedef enum { #if ADB_TRACE +#if !ADB_HOST +/* + * When running inside the emulator, guest's adbd can connect to 'adb-debug' + * qemud service that can display adb trace messages (on condition that emulator + * has been started with '-debug adb' option). + */ + +/* Delivers a trace message to the emulator via QEMU pipe. */ +void adb_qemu_trace(const char* fmt, ...); +/* Macro to use to send ADB trace messages to the emulator. */ +#define DQ(...) adb_qemu_trace(__VA_ARGS__) +#else +#define DQ(...) ((void)0) +#endif /* !ADB_HOST */ + extern int adb_trace_mask; extern unsigned char adb_trace_output_count; void adb_trace_init(void); @@ -434,6 +454,7 @@ int connection_state(atransport *t); #define CS_HOST 3 #define CS_RECOVERY 4 #define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */ +#define CS_SIDELOAD 6 extern int HOST; extern int SHELL_EXIT_NOTIFY_FD; diff --git a/adb/commandline.c b/adb/commandline.c index ffc120f..47c9bec 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -120,10 +120,12 @@ void help() " dev:<character device name>\n" " jdwp:<process pid> (remote only)\n" " adb jdwp - list PIDs of processes hosting a JDWP transport\n" - " adb install [-l] [-r] [-s] <file> - push this package file to the device and install it\n" + " adb install [-l] [-r] [-s] [--algo <algorithm name> --key <hex-encoded key> --iv <hex-encoded iv>] <file>\n" + " - push this package file to the device and install it\n" " ('-l' means forward-lock the app)\n" " ('-r' means reinstall the app, keeping its data)\n" " ('-s' means install on SD card instead of internal storage)\n" + " ('--algo', '--key', and '--iv' mean the file is encrypted already)\n" " adb uninstall [-k] <package> - remove this app package from the device\n" " ('-k' means keep the data and cache directories)\n" " adb bugreport - return all information from the device\n" @@ -367,6 +369,83 @@ static void format_host_command(char* buffer, size_t buflen, const char* comman } } +int adb_download_buffer(const char *service, const void* data, int sz, + unsigned progress) +{ + char buf[4096]; + unsigned total; + int fd; + const unsigned char *ptr; + + sprintf(buf,"%s:%d", service, sz); + fd = adb_connect(buf); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return -1; + } + + int opt = CHUNK_SIZE; + opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)); + + total = sz; + ptr = data; + + if(progress) { + char *x = strrchr(service, ':'); + if(x) service = x + 1; + } + + while(sz > 0) { + unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz; + if(writex(fd, ptr, xfer)) { + adb_status(fd); + fprintf(stderr,"* failed to write data '%s' *\n", adb_error()); + return -1; + } + sz -= xfer; + ptr += xfer; + if(progress) { + printf("sending: '%s' %4d%% \r", service, (int)(100LL - ((100LL * sz) / (total)))); + fflush(stdout); + } + } + if(progress) { + printf("\n"); + } + + if(readx(fd, buf, 4)){ + fprintf(stderr,"* error reading response *\n"); + adb_close(fd); + return -1; + } + if(memcmp(buf, "OKAY", 4)) { + buf[4] = 0; + fprintf(stderr,"* error response '%s' *\n", buf); + adb_close(fd); + return -1; + } + + adb_close(fd); + return 0; +} + + +int adb_download(const char *service, const char *fn, unsigned progress) +{ + void *data; + unsigned sz; + + data = load_file(fn, &sz); + if(data == 0) { + fprintf(stderr,"* cannot read '%s' *\n", service); + return -1; + } + + int status = adb_download_buffer(service, data, sz, progress); + free(data); + return status; +} + static void status_window(transport_type ttype, const char* serial) { char command[4096]; @@ -561,6 +640,10 @@ static int logcat(transport_type transport, char* serial, int argc, char **argv) free(quoted_log_tags); + if (!strcmp(argv[0],"longcat")) { + strncat(buf, " -v long", sizeof(buf)-1); + } + argc -= 1; argv += 1; while(argc-- > 0) { @@ -644,6 +727,7 @@ static int backup(int argc, char** argv) { return -1; } + printf("Now unlock your device and confirm the backup operation.\n"); copy_to_file(fd, outFd); adb_close(fd); @@ -671,6 +755,7 @@ static int restore(int argc, char** argv) { return -1; } + printf("Now unlock your device and confirm the restore operation.\n"); copy_to_file(tarFd, fd); adb_close(fd); @@ -1057,6 +1142,15 @@ top: return 0; } + if(!strcmp(argv[0], "sideload")) { + if(argc != 2) return usage(); + if(adb_download("sideload", argv[1], 1)) { + return 1; + } else { + return 0; + } + } + if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot") || !strcmp(argv[0], "reboot-bootloader") || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb") @@ -1225,7 +1319,7 @@ top: return 0; } - if(!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat")) { + if(!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) { return logcat(ttype, serial, argc, argv); } @@ -1440,6 +1534,7 @@ int install_app(transport_type transport, char* serial, int argc, char** argv) int file_arg = -1; int err; int i; + int verify_apk = 1; for (i = 1; i < argc; i++) { if (*argv[i] != '-') { @@ -1450,6 +1545,15 @@ int install_app(transport_type transport, char* serial, int argc, char** argv) i++; } else if (!strcmp(argv[i], "-s")) { where = SD_DEST; + } else if (!strcmp(argv[i], "--algo")) { + verify_apk = 0; + i++; + } else if (!strcmp(argv[i], "--iv")) { + verify_apk = 0; + i++; + } else if (!strcmp(argv[i], "--key")) { + verify_apk = 0; + i++; } } @@ -1481,9 +1585,9 @@ int install_app(transport_type transport, char* serial, int argc, char** argv) } } - err = do_sync_push(apk_file, apk_dest, 1 /* verify APK */); + err = do_sync_push(apk_file, apk_dest, verify_apk); if (err) { - return err; + goto cleanup_apk; } else { argv[file_arg] = apk_dest; /* destination name, not source location */ } @@ -1499,11 +1603,11 @@ int install_app(transport_type transport, char* serial, int argc, char** argv) pm_command(transport, serial, argc, argv); +cleanup_apk: if (verification_file != NULL) { delete_file(transport, serial, verification_dest); } -cleanup_apk: delete_file(transport, serial, apk_dest); return err; diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c index 862dd91..20c08d2 100644 --- a/adb/framebuffer_service.c +++ b/adb/framebuffer_service.c @@ -19,6 +19,9 @@ #include <unistd.h> #include <string.h> #include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> #include "fdevent.h" #include "adb.h" @@ -169,6 +172,8 @@ void framebuffer_service(int fd, void *cookie) if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done; done: + TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); + close(fds[0]); close(fds[1]); close(fd); diff --git a/adb/services.c b/adb/services.c index 6940be8..495a083 100644 --- a/adb/services.c +++ b/adb/services.c @@ -125,12 +125,10 @@ void restart_root_service(int fd, void *cookie) return; } + property_set("service.adb.root", "1"); snprintf(buf, sizeof(buf), "restarting adbd as root\n"); writex(fd, buf, strlen(buf)); adb_close(fd); - - // This will cause a property trigger in init.rc to restart us - property_set("service.adb.root", "1"); } } @@ -152,10 +150,6 @@ void restart_tcp_service(int fd, void *cookie) snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port); writex(fd, buf, strlen(buf)); adb_close(fd); - - // quit, and init will restart us in TCP mode - sleep(1); - exit(1); } void restart_usb_service(int fd, void *cookie) @@ -166,10 +160,6 @@ void restart_usb_service(int fd, void *cookie) snprintf(buf, sizeof(buf), "restarting in USB mode\n"); writex(fd, buf, strlen(buf)); adb_close(fd); - - // quit, and init will restart us in USB mode - sleep(1); - exit(1); } void reboot_service(int fd, void *arg) @@ -369,7 +359,6 @@ static void subproc_waiter_service(int fd, void *cookie) break; } } - usleep(100000); // poll every 0.1 sec } D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno); if (SHELL_EXIT_NOTIFY_FD >=0) { diff --git a/adb/sockets.c b/adb/sockets.c index df223b1..cd31b23 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -199,6 +199,8 @@ static void local_socket_close(asocket *s) static void local_socket_destroy(asocket *s) { apacket *p, *n; + int exit_on_close = s->exit_on_close; + D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd); /* IMPORTANT: the remove closes the fd @@ -214,6 +216,11 @@ static void local_socket_destroy(asocket *s) } remove_socket(s); free(s); + + if (exit_on_close) { + D("local_socket_destroy: exiting\n"); + exit(1); + } } @@ -418,6 +425,16 @@ asocket *create_local_service_socket(const char *name) s = create_local_socket(fd); D("LS(%d): bound to '%s' via %d\n", s->id, name, fd); + +#if !ADB_HOST + if ((!strncmp(name, "root:", 5) && getuid() != 0) + || !strncmp(name, "usb:", 4) + || !strncmp(name, "tcpip:", 6)) { + D("LS(%d): enabling exit_on_close\n", s->id); + s->exit_on_close = 1; + } +#endif + return s; } diff --git a/adb/transport.c b/adb/transport.c index 83a349a..2f7bd27 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -831,6 +831,7 @@ static const char *statename(atransport *t) case CS_DEVICE: return "device"; case CS_HOST: return "host"; case CS_RECOVERY: return "recovery"; + case CS_SIDELOAD: return "sideload"; case CS_NOPERM: return "no permissions"; default: return "unknown"; } diff --git a/adb/transport_local.c b/adb/transport_local.c index d985ee3..105c502 100644 --- a/adb/transport_local.c +++ b/adb/transport_local.c @@ -318,7 +318,7 @@ void local_init(int port) /* Running inside the device: use TCP socket as the transport. */ func = server_socket_thread; } -#endif !ADB_HOST +#endif // !ADB_HOST } D("transport: local %s init\n", HOST ? "client" : "server"); diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index a1c3523..b988115 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -117,8 +117,6 @@ #define VENDOR_ID_FUJITSU 0x04C5 // Lumigon's USB Vendor ID #define VENDOR_ID_LUMIGON 0x25E3 -//Intel's USB Vendor ID -#define VENDOR_ID_INTEL 0x8087 // Quanta's USB Vendor ID #define VENDOR_ID_QUANTA 0x0408 // INQ Mobile's USB Vendor ID @@ -127,6 +125,8 @@ #define VENDOR_ID_SONY 0x054C // Lab126's USB Vendor ID #define VENDOR_ID_LAB126 0x1949 +// Yulong Coolpad's USB Vendor ID +#define VENDOR_ID_YULONG_COOLPAD 0x1EBF /** built-in vendor list */ int builtInVendorIds[] = { @@ -171,11 +171,11 @@ int builtInVendorIds[] = { VENDOR_ID_POSITIVO, VENDOR_ID_FUJITSU, VENDOR_ID_LUMIGON, - VENDOR_ID_INTEL, VENDOR_ID_QUANTA, VENDOR_ID_INQ_MOBILE, VENDOR_ID_SONY, VENDOR_ID_LAB126, + VENDOR_ID_YULONG_COOLPAD, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c index f672336..323a09d 100644 --- a/cpio/mkbootfs.c +++ b/cpio/mkbootfs.c @@ -3,6 +3,7 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <ctype.h> #include <sys/types.h> #include <sys/stat.h> @@ -34,12 +35,51 @@ void die(const char *why, ...) exit(1); } +struct fs_config_entry { + char* name; + int uid, gid, mode; +}; + +static struct fs_config_entry* canned_config = NULL; + +/* Each line in the canned file should be a path plus three ints (uid, + * gid, mode). */ +#ifdef PATH_MAX +#define CANNED_LINE_LENGTH (PATH_MAX+100) +#else +#define CANNED_LINE_LENGTH (1024) +#endif + static int verbose = 0; static int total_size = 0; static void fix_stat(const char *path, struct stat *s) { - fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode); + if (canned_config) { + // Use the list of file uid/gid/modes loaded from the file + // given with -f. + + struct fs_config_entry* empty_path_config = NULL; + struct fs_config_entry* p; + for (p = canned_config; p->name; ++p) { + if (!p->name[0]) { + empty_path_config = p; + } + if (strcmp(p->name, path) == 0) { + s->st_uid = p->uid; + s->st_gid = p->gid; + s->st_mode = p->mode | (s->st_mode & ~07777); + return; + } + } + s->st_uid = empty_path_config->uid; + s->st_gid = empty_path_config->gid; + s->st_mode = empty_path_config->mode | (s->st_mode & ~07777); + } else { + // Use the compiled-in fs_config() function. + + fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode); + } } static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize) @@ -79,7 +119,7 @@ static void _eject(struct stat *s, char *out, int olen, char *data, unsigned dat total_size += 6 + 8*13 + olen + 1; - if(strlen(out) != olen) die("ACK!"); + if(strlen(out) != (unsigned int)olen) die("ACK!"); while(total_size & 3) { total_size++; @@ -235,11 +275,61 @@ void archive(const char *start, const char *prefix) _archive_dir(in, out, strlen(in), strlen(out)); } +static void read_canned_config(char* filename) +{ + int allocated = 8; + int used = 0; + + canned_config = + (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry)); + + char line[CANNED_LINE_LENGTH]; + FILE* f = fopen(filename, "r"); + if (f == NULL) die("failed to open canned file"); + + while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) { + if (!line[0]) break; + if (used >= allocated) { + allocated *= 2; + canned_config = (struct fs_config_entry*)realloc( + canned_config, allocated * sizeof(struct fs_config_entry)); + } + + struct fs_config_entry* cc = canned_config + used; + + if (isspace(line[0])) { + cc->name = strdup(""); + cc->uid = atoi(strtok(line, " \n")); + } else { + cc->name = strdup(strtok(line, " \n")); + cc->uid = atoi(strtok(NULL, " \n")); + } + cc->gid = atoi(strtok(NULL, " \n")); + cc->mode = strtol(strtok(NULL, " \n"), NULL, 8); + ++used; + } + if (used >= allocated) { + ++allocated; + canned_config = (struct fs_config_entry*)realloc( + canned_config, allocated * sizeof(struct fs_config_entry)); + } + canned_config[used].name = NULL; + + fclose(f); +} + + int main(int argc, char *argv[]) { argc--; argv++; + if (argc > 1 && strcmp(argv[0], "-f") == 0) { + read_canned_config(argv[1]); + argc -= 2; + argv += 2; + } + if(argc == 0) die("no directories to process?!"); while(argc-- > 0){ diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 6cfe79b..fe46706 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -5,12 +5,15 @@ ifneq ($(filter arm x86,$(TARGET_ARCH)),) LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= debuggerd.c utility.c getevent.c $(TARGET_ARCH)/machine.c $(TARGET_ARCH)/unwind.c symbol_table.c -ifeq ($(TARGET_ARCH),arm) -LOCAL_SRC_FILES += $(TARGET_ARCH)/pr-support.c -endif - -LOCAL_CFLAGS := -Wall +LOCAL_SRC_FILES:= \ + backtrace.c \ + debuggerd.c \ + getevent.c \ + tombstone.c \ + utility.c \ + $(TARGET_ARCH)/machine.c + +LOCAL_CFLAGS := -Wall -Wno-unused-parameter -std=gnu99 LOCAL_MODULE := debuggerd ifeq ($(ARCH_ARM_HAVE_VFP),true) @@ -20,7 +23,13 @@ ifeq ($(ARCH_ARM_HAVE_VFP_D32),true) LOCAL_CFLAGS += -DWITH_VFP_D32 endif # ARCH_ARM_HAVE_VFP_D32 -LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_SHARED_LIBRARIES := libcutils libc libcorkscrew + +ifeq ($(HAVE_SELINUX),true) +LOCAL_SHARED_LIBRARIES += libselinux +LOCAL_C_INCLUDES += external/libselinux/include +LOCAL_CFLAGS += -DHAVE_SELINUX +endif include $(BUILD_EXECUTABLE) diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index d5efb79..1c2e13f 100644 --- a/debuggerd/arm/machine.c +++ b/debuggerd/arm/machine.c @@ -15,29 +15,24 @@ ** limitations under the License. */ +#include <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> #include <stdio.h> #include <errno.h> -#include <signal.h> -#include <pthread.h> -#include <fcntl.h> #include <sys/types.h> -#include <dirent.h> - #include <sys/ptrace.h> -#include <sys/wait.h> -#include <sys/exec_elf.h> -#include <sys/stat.h> -#include <cutils/sockets.h> -#include <cutils/properties.h> +#include <corkscrew/ptrace.h> -#include <linux/input.h> #include <linux/user.h> -#include "utility.h" +#include "../utility.h" +#include "../machine.h" /* enable to dump memory pointed to by every register */ -#define DUMP_MEM_FOR_ALL_REGS 0 +#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 #ifdef WITH_VFP #ifdef WITH_VFP_D32 @@ -47,89 +42,7 @@ #endif #endif -/* Main entry point to get the backtrace from the crashing process */ -extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, - unsigned int sp_list[], - int *frame0_pc_sane, - bool at_fault); - -/* - * If this isn't clearly a null pointer dereference, dump the - * /proc/maps entries near the fault address. - * - * This only makes sense to do on the thread that crashed. - */ -static void show_nearby_maps(int tfd, int pid, mapinfo *map) -{ - siginfo_t si; - - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) { - _LOG(tfd, false, "cannot get siginfo for %d: %s\n", - pid, strerror(errno)); - return; - } - if (!signal_has_address(si.si_signo)) - return; - - uintptr_t addr = (uintptr_t) si.si_addr; - addr &= ~0xfff; /* round to 4K page boundary */ - if (addr == 0) /* null-pointer deref */ - return; - - _LOG(tfd, false, "\nmemory map around addr %08x:\n", si.si_addr); - - /* - * Search for a match, or for a hole where the match would be. The list - * is backward from the file content, so it starts at high addresses. - */ - bool found = false; - mapinfo *next = NULL; - mapinfo *prev = NULL; - while (map != NULL) { - if (addr >= map->start && addr < map->end) { - found = true; - next = map->next; - break; - } else if (addr >= map->end) { - /* map would be between "prev" and this entry */ - next = map; - map = NULL; - break; - } - - prev = map; - map = map->next; - } - - /* - * Show "next" then "match" then "prev" so that the addresses appear in - * ascending order (like /proc/pid/maps). - */ - if (next != NULL) { - _LOG(tfd, false, "%08x-%08x %s\n", next->start, next->end, next->name); - } else { - _LOG(tfd, false, "(no map below)\n"); - } - if (map != NULL) { - _LOG(tfd, false, "%08x-%08x %s\n", map->start, map->end, map->name); - } else { - _LOG(tfd, false, "(no map for address)\n"); - } - if (prev != NULL) { - _LOG(tfd, false, "%08x-%08x %s\n", prev->start, prev->end, prev->name); - } else { - _LOG(tfd, false, "(no map above)\n"); - } -} - -/* - * Dumps a few bytes of memory, starting a bit before and ending a bit - * after the specified address. - */ -static void dump_memory(int tfd, int pid, uintptr_t addr, - bool only_in_tombstone) -{ +static void dump_memory(log_t* log, pid_t tid, uintptr_t addr, bool at_fault) { char code_buffer[64]; /* actual 8+1+((8+1)*4) + 1 == 45 */ char ascii_buffer[32]; /* actual 16 + 1 == 17 */ uintptr_t p, end; @@ -165,7 +78,7 @@ static void dump_memory(int tfd, int pid, uintptr_t addr, * just complicates parsing and clarifies nothing for the * enlightened reader. */ - long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); + long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); int j; @@ -185,34 +98,27 @@ static void dump_memory(int tfd, int pid, uintptr_t addr, p += 4; } *asc_out = '\0'; - _LOG(tfd, only_in_tombstone, "%s %s\n", code_buffer, ascii_buffer); + _LOG(log, !at_fault, " %s %s\n", code_buffer, ascii_buffer); } - } -void dump_stack_and_code(int tfd, int pid, mapinfo *map, - int unwind_depth, unsigned int sp_list[], - bool at_fault) -{ - struct pt_regs r; - int sp_depth; - bool only_in_tombstone = !at_fault; - - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return; +/* + * If configured to do so, dump memory around *all* registers + * for the crashing thread. + */ +void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), + log_t* log, pid_t tid, bool at_fault) { + struct pt_regs regs; + if(ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + return; + } - if (DUMP_MEM_FOR_ALL_REGS && at_fault) { - /* - * If configured to do so, dump memory around *all* registers - * for the crashing thread. - * - * TODO: remove duplicates. - */ - static const char REG_NAMES[] = "R0R1R2R3R4R5R6R7R8R9SLFPIPSPLRPC"; + if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; - int reg; - for (reg = 0; reg < 16; reg++) { + for (int reg = 0; reg < 14; reg++) { /* this may not be a valid way to access, but it'll do for now */ - uintptr_t addr = r.uregs[reg]; + uintptr_t addr = regs.uregs[reg]; /* * Don't bother if it looks like a small int or ~= null, or if @@ -222,152 +128,54 @@ void dump_stack_and_code(int tfd, int pid, mapinfo *map, continue; } - _LOG(tfd, only_in_tombstone, "\nmem near %.2s:\n", - ®_NAMES[reg*2]); - dump_memory(tfd, pid, addr, false); - } - } else { - unsigned int pc, lr; - pc = r.ARM_pc; - lr = r.ARM_lr; - - _LOG(tfd, only_in_tombstone, "\ncode around pc:\n"); - dump_memory(tfd, pid, (uintptr_t) pc, only_in_tombstone); - - if (lr != pc) { - _LOG(tfd, only_in_tombstone, "\ncode around lr:\n"); - dump_memory(tfd, pid, (uintptr_t) lr, only_in_tombstone); - } - } - - if (at_fault) { - show_nearby_maps(tfd, pid, map); - } - - unsigned int p, end; - unsigned int sp = r.ARM_sp; - - p = sp - 64; - if (p > sp) - p = 0; - p &= ~3; - if (unwind_depth != 0) { - if (unwind_depth < STACK_CONTENT_DEPTH) { - end = sp_list[unwind_depth-1]; - } - else { - end = sp_list[STACK_CONTENT_DEPTH-1]; + _LOG(log, false, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, at_fault); } } - else { - end = p + 256; - /* 'end - p' has to be multiples of 4 */ - if (end < p) - end = ~7; - } - - _LOG(tfd, only_in_tombstone, "\nstack:\n"); - - /* If the crash is due to PC == 0, there will be two frames that - * have identical SP value. - */ - if (sp_list[0] == sp_list[1]) { - sp_depth = 1; - } - else { - sp_depth = 0; - } - - while (p <= end) { - char *prompt; - char level[16]; - long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - if (p == sp_list[sp_depth]) { - sprintf(level, "#%02d", sp_depth++); - prompt = level; - } - else { - prompt = " "; - } - - /* Print the stack content in the log for the first 3 frames. For the - * rest only print them in the tombstone file. - */ - _LOG(tfd, (sp_depth > 2) || only_in_tombstone, - "%s %08x %08x %s\n", prompt, p, data, - map_to_name(map, data, "")); - p += 4; - } - /* print another 64-byte of stack data after the last frame */ - - end = p+64; - /* 'end - p' has to be multiples of 4 */ - if (end < p) - end = ~7; - - while (p <= end) { - long data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL); - _LOG(tfd, (sp_depth > 2) || only_in_tombstone, - " %08x %08x %s\n", p, data, - map_to_name(map, data, "")); - p += 4; - } -} - -void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, - bool at_fault) -{ - struct pt_regs r; - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { - _LOG(tfd, !at_fault, "tid %d not responding!\n", pid); - return; - } + _LOG(log, !at_fault, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)regs.ARM_pc, at_fault); - if (unwound_level == 0) { - _LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc, - map_to_name(map, r.ARM_pc, "<unknown>")); + if (regs.ARM_pc != regs.ARM_lr) { + _LOG(log, !at_fault, "\ncode around lr:\n"); + dump_memory(log, tid, (uintptr_t)regs.ARM_lr, at_fault); } - _LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr, - map_to_name(map, r.ARM_lr, "<unknown>")); } -void dump_registers(int tfd, int pid, bool at_fault) +void dump_registers(const ptrace_context_t* context __attribute((unused)), + log_t* log, pid_t tid, bool at_fault) { struct pt_regs r; bool only_in_tombstone = !at_fault; - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { - _LOG(tfd, only_in_tombstone, - "cannot get registers: %s\n", strerror(errno)); + if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { + _LOG(log, only_in_tombstone, "cannot get registers: %s\n", strerror(errno)); return; } - _LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", - r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3); - _LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", - r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7); - _LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n", - r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp); - _LOG(tfd, only_in_tombstone, - " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", - r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr); + _LOG(log, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n", + (uint32_t)r.ARM_r0, (uint32_t)r.ARM_r1, (uint32_t)r.ARM_r2, (uint32_t)r.ARM_r3); + _LOG(log, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n", + (uint32_t)r.ARM_r4, (uint32_t)r.ARM_r5, (uint32_t)r.ARM_r6, (uint32_t)r.ARM_r7); + _LOG(log, only_in_tombstone, " r8 %08x r9 %08x sl %08x fp %08x\n", + (uint32_t)r.ARM_r8, (uint32_t)r.ARM_r9, (uint32_t)r.ARM_r10, (uint32_t)r.ARM_fp); + _LOG(log, only_in_tombstone, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + (uint32_t)r.ARM_ip, (uint32_t)r.ARM_sp, (uint32_t)r.ARM_lr, + (uint32_t)r.ARM_pc, (uint32_t)r.ARM_cpsr); #ifdef WITH_VFP struct user_vfp vfp_regs; int i; - if(ptrace(PTRACE_GETVFPREGS, pid, 0, &vfp_regs)) { - _LOG(tfd, only_in_tombstone, - "cannot get registers: %s\n", strerror(errno)); + if(ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { + _LOG(log, only_in_tombstone, "cannot get registers: %s\n", strerror(errno)); return; } for (i = 0; i < NUM_VFP_REGS; i += 2) { - _LOG(tfd, only_in_tombstone, - " d%-2d %016llx d%-2d %016llx\n", - i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); + _LOG(log, only_in_tombstone, " d%-2d %016llx d%-2d %016llx\n", + i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); } - _LOG(tfd, only_in_tombstone, " scr %08lx\n\n", vfp_regs.fpscr); + _LOG(log, only_in_tombstone, " scr %08lx\n", vfp_regs.fpscr); #endif } diff --git a/debuggerd/arm/pr-support.c b/debuggerd/arm/pr-support.c deleted file mode 100644 index 358d9b7..0000000 --- a/debuggerd/arm/pr-support.c +++ /dev/null @@ -1,345 +0,0 @@ -/* ARM EABI compliant unwinding routines - Copyright (C) 2004, 2005 Free Software Foundation, Inc. - Contributed by Paul Brook - - This file is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - In addition to the permissions in the GNU General Public License, the - Free Software Foundation gives you unlimited permission to link the - compiled version of this file into combinations with other programs, - and to distribute those combinations without any restriction coming - from the use of this file. (The General Public License restrictions - do apply in other respects; for example, they cover modification of - the file, and distribution when not linked into a combine - executable.) - - This file is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/**************************************************************************** - * The functions here are derived from gcc/config/arm/pr-support.c from the - * 4.3.x release. The main changes here involve the use of ptrace to retrieve - * memory/processor states from a remote process. - ****************************************************************************/ - -#include <sys/types.h> -#include <unwind.h> - -#include "utility.h" - -/* We add a prototype for abort here to avoid creating a dependency on - target headers. */ -extern void abort (void); - -/* Derived from _Unwind_VRS_Pop to use ptrace */ -extern _Unwind_VRS_Result -unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, - _Unwind_VRS_RegClass regclass, - _uw discriminator, - _Unwind_VRS_DataRepresentation representation, - pid_t pid); - -typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ - -/* Misc constants. */ -#define R_IP 12 -#define R_SP 13 -#define R_LR 14 -#define R_PC 15 - -#define uint32_highbit (((_uw) 1) << 31) - -void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); - -/* Unwind descriptors. */ - -typedef struct -{ - _uw16 length; - _uw16 offset; -} EHT16; - -typedef struct -{ - _uw length; - _uw offset; -} EHT32; - -/* Personality routine helper functions. */ - -#define CODE_FINISH (0xb0) - -/* Derived from next_unwind_byte to use ptrace */ -/* Return the next byte of unwinding information, or CODE_FINISH if there is - no data remaining. */ -static inline _uw8 -next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid) -{ - _uw8 b; - - if (uws->bytes_left == 0) - { - /* Load another word */ - if (uws->words_left == 0) - return CODE_FINISH; /* Nothing left. */ - uws->words_left--; - uws->data = get_remote_word(pid, uws->next); - uws->next++; - uws->bytes_left = 3; - } - else - uws->bytes_left--; - - /* Extract the most significant byte. */ - b = (uws->data >> 24) & 0xff; - uws->data <<= 8; - return b; -} - -/* Execute the unwinding instructions described by UWS. */ -_Unwind_Reason_Code -unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, - pid_t pid) -{ - _uw op; - int set_pc; - _uw reg; - - set_pc = 0; - for (;;) - { - op = next_unwind_byte_with_ptrace (uws, pid); - if (op == CODE_FINISH) - { - /* If we haven't already set pc then copy it from lr. */ - if (!set_pc) - { - _Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32, - ®); - _Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32, - ®); - set_pc = 1; - } - /* Drop out of the loop. */ - break; - } - if ((op & 0x80) == 0) - { - /* vsp = vsp +- (imm6 << 2 + 4). */ - _uw offset; - - offset = ((op & 0x3f) << 2) + 4; - _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); - if (op & 0x40) - reg -= offset; - else - reg += offset; - _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); - continue; - } - - if ((op & 0xf0) == 0x80) - { - op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid); - if (op == 0x8000) - { - /* Refuse to unwind. */ - return _URC_FAILURE; - } - /* Pop r4-r15 under mask. */ - op = (op << 4) & 0xfff0; - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32, - pid) - != _UVRSR_OK) - return _URC_FAILURE; - if (op & (1 << R_PC)) - set_pc = 1; - continue; - } - if ((op & 0xf0) == 0x90) - { - op &= 0xf; - if (op == 13 || op == 15) - /* Reserved. */ - return _URC_FAILURE; - /* vsp = r[nnnn]. */ - _Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, ®); - _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®); - continue; - } - if ((op & 0xf0) == 0xa0) - { - /* Pop r4-r[4+nnn], [lr]. */ - _uw mask; - - mask = (0xff0 >> (7 - (op & 7))) & 0xff0; - if (op & 8) - mask |= (1 << R_LR); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32, - pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - if ((op & 0xf0) == 0xb0) - { - /* op == 0xb0 already handled. */ - if (op == 0xb1) - { - op = next_unwind_byte_with_ptrace (uws, pid); - if (op == 0 || ((op & 0xf0) != 0)) - /* Spare. */ - return _URC_FAILURE; - /* Pop r0-r4 under mask. */ - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, - _UVRSD_UINT32, pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - if (op == 0xb2) - { - /* vsp = vsp + 0x204 + (uleb128 << 2). */ - int shift; - - _Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, - ®); - op = next_unwind_byte_with_ptrace (uws, pid); - shift = 2; - while (op & 0x80) - { - reg += ((op & 0x7f) << shift); - shift += 7; - op = next_unwind_byte_with_ptrace (uws, pid); - } - reg += ((op & 0x7f) << shift) + 0x204; - _Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, - ®); - continue; - } - if (op == 0xb3) - { - /* Pop VFP registers with fldmx. */ - op = next_unwind_byte_with_ptrace (uws, pid); - op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, - pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - if ((op & 0xfc) == 0xb4) - { - /* Pop FPA E[4]-E[4+nn]. */ - op = 0x40000 | ((op & 3) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, - pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - /* op & 0xf8 == 0xb8. */ - /* Pop VFP D[8]-D[8+nnn] with fldmx. */ - op = 0x80000 | ((op & 7) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - if ((op & 0xf0) == 0xc0) - { - if (op == 0xc6) - { - /* Pop iWMMXt D registers. */ - op = next_unwind_byte_with_ptrace (uws, pid); - op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, - _UVRSD_UINT64, pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - if (op == 0xc7) - { - op = next_unwind_byte_with_ptrace (uws, pid); - if (op == 0 || (op & 0xf0) != 0) - /* Spare. */ - return _URC_FAILURE; - /* Pop iWMMXt wCGR{3,2,1,0} under mask. */ - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op, - _UVRSD_UINT32, pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - if ((op & 0xf8) == 0xc0) - { - /* Pop iWMMXt wR[10]-wR[10+nnn]. */ - op = 0xa0000 | ((op & 0xf) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op, - _UVRSD_UINT64, pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - if (op == 0xc8) - { -#ifndef __VFP_FP__ - /* Pop FPA registers. */ - op = next_unwind_byte_with_ptrace (uws, pid); - op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX, - pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; -#else - /* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */ - op = next_unwind_byte_with_ptrace (uws, pid); - op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, - _UVRSD_DOUBLE, pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; -#endif - } - if (op == 0xc9) - { - /* Pop VFP registers with fldmd. */ - op = next_unwind_byte_with_ptrace (uws, pid); - op = ((op & 0xf0) << 12) | ((op & 0xf) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, - _UVRSD_DOUBLE, pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - /* Spare. */ - return _URC_FAILURE; - } - if ((op & 0xf8) == 0xd0) - { - /* Pop VFP D[8]-D[8+nnn] with fldmd. */ - op = 0x80000 | ((op & 7) + 1); - if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE, - pid) - != _UVRSR_OK) - return _URC_FAILURE; - continue; - } - /* Spare. */ - return _URC_FAILURE; - } - return _URC_OK; -} diff --git a/debuggerd/arm/unwind.c b/debuggerd/arm/unwind.c deleted file mode 100644 index d9600b7..0000000 --- a/debuggerd/arm/unwind.c +++ /dev/null @@ -1,667 +0,0 @@ -/* ARM EABI compliant unwinding routines. - Copyright (C) 2004, 2005 Free Software Foundation, Inc. - Contributed by Paul Brook - - This file is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - In addition to the permissions in the GNU General Public License, the - Free Software Foundation gives you unlimited permission to link the - compiled version of this file into combinations with other programs, - and to distribute those combinations without any restriction coming - from the use of this file. (The General Public License restrictions - do apply in other respects; for example, they cover modification of - the file, and distribution when not linked into a combine - executable.) - - This file is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/**************************************************************************** - * The functions here are derived from gcc/config/arm/unwind-arm.c from the - * 4.3.x release. The main changes here involve the use of ptrace to retrieve - * memory/processor states from a remote process. - ****************************************************************************/ - -#include <cutils/logd.h> -#include <sys/ptrace.h> -#include <unwind.h> -#include "utility.h" - -#include "symbol_table.h" - -typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */ - -void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp); -bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp); -bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp, - const type_info *rttip, - bool is_reference, - void **matched_object); - -/* Misc constants. */ -#define R_IP 12 -#define R_SP 13 -#define R_LR 14 -#define R_PC 15 - -#define EXIDX_CANTUNWIND 1 -#define uint32_highbit (((_uw) 1) << 31) - -#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1) -#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2) -#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3) -#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4) - -struct core_regs -{ - _uw r[16]; -}; - -/* We use normal integer types here to avoid the compiler generating - coprocessor instructions. */ -struct vfp_regs -{ - _uw64 d[16]; - _uw pad; -}; - -struct vfpv3_regs -{ - /* Always populated via VSTM, so no need for the "pad" field from - vfp_regs (which is used to store the format word for FSTMX). */ - _uw64 d[16]; -}; - -struct fpa_reg -{ - _uw w[3]; -}; - -struct fpa_regs -{ - struct fpa_reg f[8]; -}; - -struct wmmxd_regs -{ - _uw64 wd[16]; -}; - -struct wmmxc_regs -{ - _uw wc[4]; -}; - -/* Unwind descriptors. */ - -typedef struct -{ - _uw16 length; - _uw16 offset; -} EHT16; - -typedef struct -{ - _uw length; - _uw offset; -} EHT32; - -/* The ABI specifies that the unwind routines may only use core registers, - except when actually manipulating coprocessor state. This allows - us to write one implementation that works on all platforms by - demand-saving coprocessor registers. - - During unwinding we hold the coprocessor state in the actual hardware - registers and allocate demand-save areas for use during phase1 - unwinding. */ - -typedef struct -{ - /* The first fields must be the same as a phase2_vrs. */ - _uw demand_save_flags; - struct core_regs core; - _uw prev_sp; /* Only valid during forced unwinding. */ - struct vfp_regs vfp; - struct vfpv3_regs vfp_regs_16_to_31; - struct fpa_regs fpa; - struct wmmxd_regs wmmxd; - struct wmmxc_regs wmmxc; -} phase1_vrs; - -/* This must match the structure created by the assembly wrappers. */ -typedef struct -{ - _uw demand_save_flags; - struct core_regs core; -} phase2_vrs; - - -/* An exception index table entry. */ - -typedef struct __EIT_entry -{ - _uw fnoffset; - _uw content; -} __EIT_entry; - -/* Derived version to use ptrace */ -typedef _Unwind_Reason_Code (*personality_routine_with_ptrace) - (_Unwind_State, - _Unwind_Control_Block *, - _Unwind_Context *, - pid_t); - -/* Derived version to use ptrace */ -/* ABI defined personality routines. */ -static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State, - _Unwind_Control_Block *, _Unwind_Context *, pid_t); -static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State, - _Unwind_Control_Block *, _Unwind_Context *, pid_t); -static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State, - _Unwind_Control_Block *, _Unwind_Context *, pid_t); - -/* Execute the unwinding instructions described by UWS. */ -extern _Unwind_Reason_Code -unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws, - pid_t pid); - -/* Derived version to use ptrace. Only handles core registers. Disregards - * FP and others. - */ -/* ABI defined function to pop registers off the stack. */ - -_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context, - _Unwind_VRS_RegClass regclass, - _uw discriminator, - _Unwind_VRS_DataRepresentation representation, - pid_t pid) -{ - phase1_vrs *vrs = (phase1_vrs *) context; - - switch (regclass) - { - case _UVRSC_CORE: - { - _uw *ptr; - _uw mask; - int i; - - if (representation != _UVRSD_UINT32) - return _UVRSR_FAILED; - - mask = discriminator & 0xffff; - ptr = (_uw *) vrs->core.r[R_SP]; - /* Pop the requested registers. */ - for (i = 0; i < 16; i++) - { - if (mask & (1 << i)) { - vrs->core.r[i] = get_remote_word(pid, ptr); - ptr++; - } - } - /* Writeback the stack pointer value if it wasn't restored. */ - if ((mask & (1 << R_SP)) == 0) - vrs->core.r[R_SP] = (_uw) ptr; - } - return _UVRSR_OK; - - default: - return _UVRSR_FAILED; - } -} - -/* Core unwinding functions. */ - -/* Calculate the address encoded by a 31-bit self-relative offset at address - P. */ -static inline _uw -selfrel_offset31 (const _uw *p, pid_t pid) -{ - _uw offset = get_remote_word(pid, (void*)p); - - //offset = *p; - /* Sign extend to 32 bits. */ - if (offset & (1 << 30)) - offset |= 1u << 31; - else - offset &= ~(1u << 31); - - return offset + (_uw) p; -} - - -/* Perform a binary search for RETURN_ADDRESS in TABLE. The table contains - NREC entries. */ - -static const __EIT_entry * -search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address, - pid_t pid) -{ - _uw next_fn; - _uw this_fn; - int n, left, right; - - if (nrec == 0) - return (__EIT_entry *) 0; - - left = 0; - right = nrec - 1; - - while (1) - { - n = (left + right) / 2; - this_fn = selfrel_offset31 (&table[n].fnoffset, pid); - if (n != nrec - 1) - next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1; - else - next_fn = (_uw)0 - 1; - - if (return_address < this_fn) - { - if (n == left) - return (__EIT_entry *) 0; - right = n - 1; - } - else if (return_address <= next_fn) - return &table[n]; - else - left = n + 1; - } -} - -/* Find the exception index table eintry for the given address. */ -static const __EIT_entry* -get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map) -{ - const __EIT_entry *eitp = NULL; - int nrec; - mapinfo *mi; - - /* The return address is the address of the instruction following the - call instruction (plus one in thumb mode). If this was the last - instruction in the function the address will lie in the following - function. Subtract 2 from the address so that it points within the call - instruction itself. */ - if (return_address >= 2) - return_address -= 2; - - for (mi = map; mi != NULL; mi = mi->next) { - if (return_address >= mi->start && return_address <= mi->end) break; - } - - if (mi) { - if (containing_map) *containing_map = mi; - eitp = (__EIT_entry *) mi->exidx_start; - nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry); - eitp = search_EIT_table (eitp, nrec, return_address, pid); - } - return eitp; -} - -/* Find the exception index table eintry for the given address. - Fill in the relevant fields of the UCB. - Returns _URC_FAILURE if an error occurred, _URC_OK on success. */ - -static _Unwind_Reason_Code -get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid, - mapinfo *map, mapinfo **containing_map) -{ - const __EIT_entry *eitp; - - eitp = get_eitp(return_address, pid, map, containing_map); - - if (!eitp) - { - UCB_PR_ADDR (ucbp) = 0; - return _URC_FAILURE; - } - ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid); - - _uw eitp_content = get_remote_word(pid, (void *)&eitp->content); - - /* Can this frame be unwound at all? */ - if (eitp_content == EXIDX_CANTUNWIND) - { - UCB_PR_ADDR (ucbp) = 0; - return _URC_END_OF_STACK; - } - - /* Obtain the address of the "real" __EHT_Header word. */ - - if (eitp_content & uint32_highbit) - { - /* It is immediate data. */ - ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content; - ucbp->pr_cache.additional = 1; - } - else - { - /* The low 31 bits of the content field are a self-relative - offset to an _Unwind_EHT_Entry structure. */ - ucbp->pr_cache.ehtp = - (_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid); - ucbp->pr_cache.additional = 0; - } - - /* Discover the personality routine address. */ - if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31)) - { - /* One of the predefined standard routines. */ - _uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf; - if (idx == 0) - UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace; - else if (idx == 1) - UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace; - else if (idx == 2) - UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace; - else - { /* Failed */ - UCB_PR_ADDR (ucbp) = 0; - return _URC_FAILURE; - } - } - else - { - /* Execute region offset to PR */ - UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid); - /* Since we are unwinding the stack from a different process, it is - * impossible to execute the personality routine in debuggerd. Punt here. - */ - return _URC_FAILURE; - } - return _URC_OK; -} - -/* Print out the current call level, pc, and module name in the crash log */ -static _Unwind_Reason_Code log_function(_Unwind_Context *context, pid_t pid, - int tfd, - int stack_level, - mapinfo *map, - unsigned int sp_list[], - bool at_fault) -{ - _uw pc; - _uw rel_pc; - phase2_vrs *vrs = (phase2_vrs*) context; - const mapinfo *mi; - bool only_in_tombstone = !at_fault; - const struct symbol* sym = 0; - - if (stack_level < STACK_CONTENT_DEPTH) { - sp_list[stack_level] = vrs->core.r[R_SP]; - } - pc = vrs->core.r[R_PC]; - - // Top level frame - if (stack_level == 0) { - pc &= ~1; - } - // For deeper framers, rollback pc by one instruction - else { - pc = vrs->core.r[R_PC]; - /* Thumb mode - need to check whether the bl(x) has long offset or not. - * Examples: - * - * arm blx in the middle of thumb: - * 187ae: 2300 movs r3, #0 - * 187b0: f7fe ee1c blx 173ec - * 187b4: 2c00 cmp r4, #0 - * - * arm bl in the middle of thumb: - * 187d8: 1c20 adds r0, r4, #0 - * 187da: f136 fd15 bl 14f208 - * 187de: 2800 cmp r0, #0 - * - * pure thumb: - * 18894: 189b adds r3, r3, r2 - * 18896: 4798 blx r3 - * 18898: b001 add sp, #4 - */ - if (pc & 1) { - _uw prev_word; - pc = (pc & ~1); - prev_word = get_remote_word(pid, (char *) pc-4); - // Long offset - if ((prev_word & 0xf0000000) == 0xf0000000 && - (prev_word & 0x0000e000) == 0x0000e000) { - pc -= 4; - } - else { - pc -= 2; - } - } - else { - pc -= 4; - } - } - - /* We used to print the absolute PC in the back trace, and mask out the top - * 3 bits to guesstimate the offset in the .so file. This is not working for - * non-prelinked libraries since the starting offset may not be aligned on - * 1MB boundaries, and the library may be larger than 1MB. So for .so - * addresses we print the relative offset in back trace. - */ - mi = pc_to_mapinfo(map, pc, &rel_pc); - - /* See if we can determine what symbol this stack frame resides in */ - if (mi != 0 && mi->symbols != 0) { - sym = symbol_table_lookup(mi->symbols, rel_pc); - } - - if (sym) { - _LOG(tfd, only_in_tombstone, - " #%02d pc %08x %s (%s)\n", stack_level, rel_pc, - mi ? mi->name : "", sym->name); - } else { - _LOG(tfd, only_in_tombstone, - " #%02d pc %08x %s\n", stack_level, rel_pc, - mi ? mi->name : ""); - } - - return _URC_NO_REASON; -} - -/* Derived from __gnu_Unwind_Backtrace to use ptrace */ -/* Perform stack backtrace through unwind data. Return the level of stack it - * unwinds. - */ -int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, - unsigned int sp_list[], int *frame0_pc_sane, - bool at_fault) -{ - phase1_vrs saved_vrs; - _Unwind_Reason_Code code = _URC_OK; - struct pt_regs r; - int i; - int stack_level = 0; - - _Unwind_Control_Block ucb; - _Unwind_Control_Block *ucbp = &ucb; - - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0; - - for (i = 0; i < 16; i++) { - saved_vrs.core.r[i] = r.uregs[i]; - /* - _LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]); - */ - } - - /* Set demand-save flags. */ - saved_vrs.demand_save_flags = ~(_uw) 0; - - /* - * If the app crashes because of calling the weeds, we cannot pass the PC - * to the usual unwinding code as the EXIDX mapping will fail. - * Instead, we simply print out the 0 as the top frame, and resume the - * unwinding process with the value stored in LR. - */ - if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) { - *frame0_pc_sane = 0; - log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, - map, sp_list, at_fault); - saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR]; - stack_level++; - } - - do { - mapinfo *this_map = NULL; - /* Find the entry for this routine. */ - if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map) - != _URC_OK) { - /* Uncomment the code below to study why the unwinder failed */ -#if 0 - /* Shed more debugging info for stack unwinder improvement */ - if (this_map) { - _LOG(tfd, 1, - "Relative PC=%#x from %s not contained in EXIDX\n", - saved_vrs.core.r[R_PC] - this_map->start, this_map->name); - } - _LOG(tfd, 1, "PC=%#x SP=%#x\n", - saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]); -#endif - code = _URC_FAILURE; - break; - } - - /* The dwarf unwinder assumes the context structure holds things - like the function and LSDA pointers. The ARM implementation - caches these in the exception header (UCB). To avoid - rewriting everything we make the virtual IP register point at - the UCB. */ - _Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp); - - /* Call log function. */ - if (log_function ((_Unwind_Context *) &saved_vrs, pid, tfd, stack_level, - map, sp_list, at_fault) != _URC_NO_REASON) { - code = _URC_FAILURE; - break; - } - stack_level++; - - /* Call the pr to decide what to do. */ - code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))( - _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp, - (void *) &saved_vrs, pid); - /* - * In theory the unwinding process will stop when the end of stack is - * reached or there is no unwinding information for the code address. - * To add another level of guarantee that the unwinding process - * will terminate we will stop it when the STACK_CONTENT_DEPTH is reached. - */ - } while (code != _URC_END_OF_STACK && code != _URC_FAILURE && - stack_level < STACK_CONTENT_DEPTH); - return stack_level; -} - - -/* Derived version to use ptrace */ -/* Common implementation for ARM ABI defined personality routines. - ID is the index of the personality routine, other arguments are as defined - by __aeabi_unwind_cpp_pr{0,1,2}. */ - -static _Unwind_Reason_Code -unwind_pr_common_with_ptrace (_Unwind_State state, - _Unwind_Control_Block *ucbp, - _Unwind_Context *context, - int id, - pid_t pid) -{ - __gnu_unwind_state uws; - _uw *data; - int phase2_call_unexpected_after_unwind = 0; - - state &= _US_ACTION_MASK; - - data = (_uw *) ucbp->pr_cache.ehtp; - uws.data = get_remote_word(pid, data); - data++; - uws.next = data; - if (id == 0) - { - uws.data <<= 8; - uws.words_left = 0; - uws.bytes_left = 3; - } - else - { - uws.words_left = (uws.data >> 16) & 0xff; - uws.data <<= 16; - uws.bytes_left = 2; - data += uws.words_left; - } - - /* Restore the saved pointer. */ - if (state == _US_UNWIND_FRAME_RESUME) - data = (_uw *) ucbp->cleanup_cache.bitpattern[0]; - - if ((ucbp->pr_cache.additional & 1) == 0) - { - /* Process descriptors. */ - while (get_remote_word(pid, data)) { - /********************************************************************** - * The original code here seems to deal with exceptions that are not - * applicable in our toolchain, thus there is no way to test it for now. - * Instead of leaving it here and causing potential instability in - * debuggerd, we'd better punt here and leave the stack unwound. - * In the future when we discover cases where the stack should be unwound - * further but is not, we can revisit the code here. - **********************************************************************/ - return _URC_FAILURE; - } - /* Finished processing this descriptor. */ - } - - if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK) - return _URC_FAILURE; - - if (phase2_call_unexpected_after_unwind) - { - /* Enter __cxa_unexpected as if called from the call site. */ - _Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC)); - _Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected); - return _URC_INSTALL_CONTEXT; - } - - return _URC_CONTINUE_UNWIND; -} - - -/* ABI defined personality routine entry points. */ - -static _Unwind_Reason_Code -unwind_cpp_pr0_with_ptrace (_Unwind_State state, - _Unwind_Control_Block *ucbp, - _Unwind_Context *context, - pid_t pid) -{ - return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid); -} - -static _Unwind_Reason_Code -unwind_cpp_pr1_with_ptrace (_Unwind_State state, - _Unwind_Control_Block *ucbp, - _Unwind_Context *context, - pid_t pid) -{ - return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid); -} - -static _Unwind_Reason_Code -unwind_cpp_pr2_with_ptrace (_Unwind_State state, - _Unwind_Control_Block *ucbp, - _Unwind_Context *context, - pid_t pid) -{ - return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid); -} diff --git a/debuggerd/backtrace.c b/debuggerd/backtrace.c new file mode 100644 index 0000000..62f7f32 --- /dev/null +++ b/debuggerd/backtrace.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <errno.h> +#include <limits.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/ptrace.h> + +#include <corkscrew/backtrace.h> + +#include "tombstone.h" +#include "utility.h" + +#define STACK_DEPTH 32 + +static void dump_process_header(log_t* log, pid_t pid) { + char path[PATH_MAX]; + char procnamebuf[1024]; + char* procname = NULL; + FILE* fp; + + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if ((fp = fopen(path, "r"))) { + procname = fgets(procnamebuf, sizeof(procnamebuf), fp); + fclose(fp); + } + + time_t t = time(NULL); + struct tm tm; + localtime_r(&t, &tm); + char timestr[64]; + strftime(timestr, sizeof(timestr), "%F %T", &tm); + _LOG(log, false, "\n\n----- pid %d at %s -----\n", pid, timestr); + + if (procname) { + _LOG(log, false, "Cmd line: %s\n", procname); + } +} + +static void dump_process_footer(log_t* log, pid_t pid) { + _LOG(log, false, "\n----- end %d -----\n", pid); +} + +static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool attached, + bool* detach_failed, int* total_sleep_time_usec) { + char path[PATH_MAX]; + char threadnamebuf[1024]; + char* threadname = NULL; + FILE* fp; + + snprintf(path, sizeof(path), "/proc/%d/comm", tid); + if ((fp = fopen(path, "r"))) { + threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); + fclose(fp); + if (threadname) { + size_t len = strlen(threadname); + if (len && threadname[len - 1] == '\n') { + threadname[len - 1] = '\0'; + } + } + } + + _LOG(log, false, "\n\"%s\" sysTid=%d\n", threadname ? threadname : "<unknown>", tid); + + if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { + _LOG(log, false, "Could not attach to thread: %s\n", strerror(errno)); + return; + } + + wait_for_stop(tid, total_sleep_time_usec); + + backtrace_frame_t backtrace[STACK_DEPTH]; + ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH); + if (frames <= 0) { + _LOG(log, false, "Could not obtain stack trace for thread.\n"); + } else { + backtrace_symbol_t backtrace_symbols[STACK_DEPTH]; + get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols); + for (size_t i = 0; i < (size_t)frames; i++) { + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + _LOG(log, false, " %s\n", line); + } + free_backtrace_symbols(backtrace_symbols, frames); + } + + if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { + LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno)); + *detach_failed = true; + } +} + +void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed, + int* total_sleep_time_usec) { + log_t log; + log.tfd = fd; + log.quiet = true; + + ptrace_context_t* context = load_ptrace_context(tid); + dump_process_header(&log, pid); + dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec); + + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + DIR* d = opendir(task_path); + if (d) { + struct dirent debuf; + struct dirent *de; + while (!readdir_r(d, &debuf, &de) && de) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { + continue; + } + + char* end; + pid_t new_tid = strtoul(de->d_name, &end, 10); + if (*end || new_tid == tid) { + continue; + } + + dump_thread(&log, new_tid, context, false, detach_failed, total_sleep_time_usec); + } + closedir(d); + } + + dump_process_footer(&log, pid); + free_ptrace_context(context); +} diff --git a/nexus/LoopController.h b/debuggerd/backtrace.h index 53d16f1..ec7d20f 100644 --- a/nexus/LoopController.h +++ b/debuggerd/backtrace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,18 @@ * limitations under the License. */ -#ifndef _LOOP_CONTROLLER_H -#define _LOOP_CONTROLLER_H +#ifndef _DEBUGGERD_BACKTRACE_H +#define _DEBUGGERD_BACKTRACE_H -#include "Controller.h" +#include <stddef.h> +#include <stdbool.h> +#include <sys/types.h> -class ControllerHandler; +#include <corkscrew/ptrace.h> -class LoopController : public Controller { -public: - LoopController(PropertyManager *propmngr, IControllerHandler *h); - virtual ~LoopController() {} +/* Dumps a backtrace using a format similar to what Dalvik uses so that the result + * can be intermixed in a bug report. */ +void dump_backtrace(int fd, pid_t pid, pid_t tid, bool* detach_failed, + int* total_sleep_time_usec); - int set(const char *name, const char *value); - const char *get(const char *name, char *buffer, size_t maxsize); -}; - -#endif +#endif // _DEBUGGERD_BACKTRACE_H diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 2acf26d..8009631 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -23,591 +23,36 @@ #include <fcntl.h> #include <sys/types.h> #include <dirent.h> +#include <time.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/exec_elf.h> #include <sys/stat.h> +#include <sys/poll.h> #include <cutils/sockets.h> #include <cutils/logd.h> #include <cutils/logger.h> #include <cutils/properties.h> +#include <cutils/debugger.h> + +#include <corkscrew/backtrace.h> #include <linux/input.h> #include <private/android_filesystem_config.h> -#include "debuggerd.h" +#include "backtrace.h" +#include "getevent.h" +#include "tombstone.h" #include "utility.h" -#define ANDROID_LOG_INFO 4 - -void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) - __attribute__ ((format(printf, 3, 4))); - -/* Log information onto the tombstone */ -void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...) -{ - char buf[512]; - - va_list ap; - va_start(ap, fmt); - - if (tfd >= 0) { - int len; - vsnprintf(buf, sizeof(buf), fmt, ap); - len = strlen(buf); - if(tfd >= 0) write(tfd, buf, len); - } - - if (!in_tombstone_only) - __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); - va_end(ap); -} - -// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so -// 012345678901234567890123456789012345678901234567890123456789 -// 0 1 2 3 4 5 - -mapinfo *parse_maps_line(char *line) -{ - mapinfo *mi; - int len = strlen(line); - - if (len < 1) return 0; /* not expected */ - line[--len] = 0; - - if (len < 50) { - mi = malloc(sizeof(mapinfo) + 1); - } else { - mi = malloc(sizeof(mapinfo) + (len - 47)); - } - if (mi == 0) return 0; - - mi->isExecutable = (line[20] == 'x'); - - mi->start = strtoul(line, 0, 16); - mi->end = strtoul(line + 9, 0, 16); - /* To be filled in parse_elf_info if the mapped section starts with - * elf_header - */ - mi->exidx_start = mi->exidx_end = 0; - mi->symbols = 0; - mi->next = 0; - if (len < 50) { - mi->name[0] = '\0'; - } else { - strcpy(mi->name, line + 49); - } - - return mi; -} - -void dump_build_info(int tfd) -{ - char fingerprint[PROPERTY_VALUE_MAX]; - - property_get("ro.build.fingerprint", fingerprint, "unknown"); - - _LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint); -} - -const char *get_signame(int sig) -{ - switch(sig) { - case SIGILL: return "SIGILL"; - case SIGABRT: return "SIGABRT"; - case SIGBUS: return "SIGBUS"; - case SIGFPE: return "SIGFPE"; - case SIGSEGV: return "SIGSEGV"; - case SIGSTKFLT: return "SIGSTKFLT"; - default: return "?"; - } -} - -const char *get_sigcode(int signo, int code) -{ - switch (signo) { - case SIGILL: - switch (code) { - case ILL_ILLOPC: return "ILL_ILLOPC"; - case ILL_ILLOPN: return "ILL_ILLOPN"; - case ILL_ILLADR: return "ILL_ILLADR"; - case ILL_ILLTRP: return "ILL_ILLTRP"; - case ILL_PRVOPC: return "ILL_PRVOPC"; - case ILL_PRVREG: return "ILL_PRVREG"; - case ILL_COPROC: return "ILL_COPROC"; - case ILL_BADSTK: return "ILL_BADSTK"; - } - break; - case SIGBUS: - switch (code) { - case BUS_ADRALN: return "BUS_ADRALN"; - case BUS_ADRERR: return "BUS_ADRERR"; - case BUS_OBJERR: return "BUS_OBJERR"; - } - break; - case SIGFPE: - switch (code) { - case FPE_INTDIV: return "FPE_INTDIV"; - case FPE_INTOVF: return "FPE_INTOVF"; - case FPE_FLTDIV: return "FPE_FLTDIV"; - case FPE_FLTOVF: return "FPE_FLTOVF"; - case FPE_FLTUND: return "FPE_FLTUND"; - case FPE_FLTRES: return "FPE_FLTRES"; - case FPE_FLTINV: return "FPE_FLTINV"; - case FPE_FLTSUB: return "FPE_FLTSUB"; - } - break; - case SIGSEGV: - switch (code) { - case SEGV_MAPERR: return "SEGV_MAPERR"; - case SEGV_ACCERR: return "SEGV_ACCERR"; - } - break; - } - return "?"; -} - -void dump_fault_addr(int tfd, int pid, int sig) -{ - siginfo_t si; - - memset(&si, 0, sizeof(si)); - if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){ - _LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno)); - } else if (signal_has_address(sig)) { - _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr %08x\n", - sig, get_signame(sig), - si.si_code, get_sigcode(sig, si.si_code), - (uintptr_t) si.si_addr); - } else { - _LOG(tfd, false, "signal %d (%s), code %d (%s), fault addr --------\n", - sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); - } -} - -void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig) -{ - char data[1024]; - char *x = 0; - FILE *fp; - - sprintf(data, "/proc/%d/cmdline", pid); - fp = fopen(data, "r"); - if(fp) { - x = fgets(data, 1024, fp); - fclose(fp); - } - - _LOG(tfd, false, - "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); - dump_build_info(tfd); - _LOG(tfd, false, "pid: %d, tid: %d >>> %s <<<\n", - pid, tid, x ? x : "UNKNOWN"); - - if(sig) dump_fault_addr(tfd, tid, sig); -} - -static void parse_elf_info(mapinfo *milist, pid_t pid) -{ - mapinfo *mi; - for (mi = milist; mi != NULL; mi = mi->next) { - if (!mi->isExecutable) - continue; - - Elf32_Ehdr ehdr; - - memset(&ehdr, 0, sizeof(Elf32_Ehdr)); - /* Read in sizeof(Elf32_Ehdr) worth of data from the beginning of - * mapped section. - */ - get_remote_struct(pid, (void *) (mi->start), &ehdr, - sizeof(Elf32_Ehdr)); - /* Check if it has the matching magic words */ - if (IS_ELF(ehdr)) { - Elf32_Phdr phdr; - Elf32_Phdr *ptr; - int i; - - 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, - sizeof(Elf32_Phdr)); -#ifdef __arm__ - /* Found a EXIDX segment? */ - if (phdr.p_type == PT_ARM_EXIDX) { - mi->exidx_start = mi->start + phdr.p_offset; - mi->exidx_end = mi->exidx_start + phdr.p_filesz; - break; - } -#endif - } - - /* Try to load symbols from this file */ - mi->symbols = symbol_table_create(mi->name); - } - } -} - -void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault) -{ - char data[1024]; - FILE *fp; - mapinfo *milist = 0; - unsigned int sp_list[STACK_CONTENT_DEPTH]; - int stack_depth; -#ifdef __arm__ - int frame0_pc_sane = 1; -#endif - - if (!at_fault) { - _LOG(tfd, true, - "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); - _LOG(tfd, true, "pid: %d, tid: %d\n", pid, tid); - } - - dump_registers(tfd, tid, at_fault); - - /* Clear stack pointer records */ - memset(sp_list, 0, sizeof(sp_list)); - - sprintf(data, "/proc/%d/maps", pid); - fp = fopen(data, "r"); - if(fp) { - while(fgets(data, 1024, fp)) { - mapinfo *mi = parse_maps_line(data); - if(mi) { - mi->next = milist; - milist = mi; - } - } - fclose(fp); - } - - parse_elf_info(milist, tid); - -#if __arm__ - /* If stack unwinder fails, use the default solution to dump the stack - * content. - */ - stack_depth = unwind_backtrace_with_ptrace(tfd, tid, milist, sp_list, - &frame0_pc_sane, at_fault); - - /* The stack unwinder should at least unwind two levels of stack. If less - * level is seen we make sure at lease pc and lr are dumped. - */ - if (stack_depth < 2) { - dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault); - } - - dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, at_fault); -#elif __i386__ - /* If stack unwinder fails, use the default solution to dump the stack - * content. - */ - stack_depth = unwind_backtrace_with_ptrace_x86(tfd, tid, milist,at_fault); -#else -#error "Unsupported architecture" -#endif - - while(milist) { - mapinfo *next = milist->next; - symbol_table_free(milist->symbols); - free(milist); - milist = next; - } -} - -#define MAX_TOMBSTONES 10 - -#define typecheck(x,y) { \ - typeof(x) __dummy1; \ - typeof(y) __dummy2; \ - (void)(&__dummy1 == &__dummy2); } - -#define TOMBSTONE_DIR "/data/tombstones" - -/* - * find_and_open_tombstone - find an available tombstone slot, if any, of the - * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no - * file is available, we reuse the least-recently-modified file. - */ -static int find_and_open_tombstone(void) -{ - unsigned long mtime = ULONG_MAX; - struct stat sb; - char path[128]; - int fd, i, oldest = 0; - - /* - * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought - * to, our logic breaks. This check will generate a warning if that happens. - */ - typecheck(mtime, sb.st_mtime); - - /* - * In a single wolf-like pass, find an available slot and, in case none - * exist, find and record the least-recently-modified file. - */ - for (i = 0; i < MAX_TOMBSTONES; i++) { - snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); - - if (!stat(path, &sb)) { - if (sb.st_mtime < mtime) { - oldest = i; - mtime = sb.st_mtime; - } - continue; - } - if (errno != ENOENT) - continue; - - fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); - if (fd < 0) - continue; /* raced ? */ - - fchown(fd, AID_SYSTEM, AID_SYSTEM); - return fd; - } - - /* we didn't find an available file, so we clobber the oldest one */ - snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); - fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - fchown(fd, AID_SYSTEM, AID_SYSTEM); - - return fd; -} - -/* Return true if some thread is not detached cleanly */ -static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid) -{ - char task_path[1024]; - - sprintf(task_path, "/proc/%d/task", pid); - DIR *d; - struct dirent *de; - int need_cleanup = 0; - - d = opendir(task_path); - /* Bail early if cannot open the task directory */ - if (d == NULL) { - XLOG("Cannot open /proc/%d/task\n", pid); - return false; - } - while ((de = readdir(d)) != NULL) { - unsigned new_tid; - /* Ignore "." and ".." */ - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) - continue; - new_tid = atoi(de->d_name); - /* The main thread at fault has been handled individually */ - if (new_tid == tid) - continue; - - /* Skip this thread if cannot ptrace it */ - if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) - continue; - - dump_crash_report(tfd, pid, new_tid, false); - - if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { - XLOG("detach of tid %d failed: %s\n", new_tid, strerror(errno)); - need_cleanup = 1; - } - } - closedir(d); - - return need_cleanup != 0; -} - -/* - * Reads the contents of the specified log device, filters out the entries - * that don't match the specified pid, and writes them to the tombstone file. - * - * If "tailOnly" is set, we only print the last few lines. - */ -static void dump_log_file(int tfd, unsigned pid, const char* filename, - bool tailOnly) -{ - bool first = true; - - /* circular buffer, for "tailOnly" mode */ - const int kShortLogMaxLines = 5; - const int kShortLogLineLen = 256; - char shortLog[kShortLogMaxLines][kShortLogLineLen]; - int shortLogCount = 0; - int shortLogNext = 0; - - int logfd = open(filename, O_RDONLY | O_NONBLOCK); - if (logfd < 0) { - XLOG("Unable to open %s: %s\n", filename, strerror(errno)); - return; - } - - union { - unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; - struct logger_entry entry; - } log_entry; - - while (true) { - ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); - if (actual < 0) { - if (errno == EINTR) { - /* interrupted by signal, retry */ - continue; - } else if (errno == EAGAIN) { - /* non-blocking EOF; we're done */ - break; - } else { - _LOG(tfd, true, "Error while reading log: %s\n", - strerror(errno)); - break; - } - } else if (actual == 0) { - _LOG(tfd, true, "Got zero bytes while reading log: %s\n", - strerror(errno)); - break; - } - - /* - * NOTE: if you XLOG something here, this will spin forever, - * because you will be writing as fast as you're reading. Any - * high-frequency debug diagnostics should just be written to - * the tombstone file. - */ - - struct logger_entry* entry = &log_entry.entry; - - if (entry->pid != (int32_t) pid) { - /* wrong pid, ignore */ - continue; - } - - if (first) { - _LOG(tfd, true, "--------- %slog %s\n", - tailOnly ? "tail end of " : "", filename); - first = false; - } - - /* - * Msg format is: <priority:1><tag:N>\0<message:N>\0 - * - * We want to display it in the same format as "logcat -v threadtime" - * (although in this case the pid is redundant). - * - * TODO: scan for line breaks ('\n') and display each text line - * on a separate line, prefixed with the header, like logcat does. - */ - static const char* kPrioChars = "!.VDIWEFS"; - unsigned char prio = entry->msg[0]; - char* tag = entry->msg + 1; - char* msg = tag + strlen(tag) + 1; - - /* consume any trailing newlines */ - char* eatnl = msg + strlen(msg) - 1; - while (eatnl >= msg && *eatnl == '\n') { - *eatnl-- = '\0'; - } - - char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); - - char timeBuf[32]; - time_t sec = (time_t) entry->sec; - struct tm tmBuf; - struct tm* ptm; - ptm = localtime_r(&sec, &tmBuf); - strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - - if (tailOnly) { - snprintf(shortLog[shortLogNext], kShortLogLineLen, - "%s.%03d %5d %5d %c %-8s: %s", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - shortLogCount++; - } else { - _LOG(tfd, true, "%s.%03d %5d %5d %c %-8s: %s\n", - timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, - prioChar, tag, msg); - } - } - - if (tailOnly) { - int i; - - /* - * If we filled the buffer, we want to start at "next", which has - * the oldest entry. If we didn't, we want to start at zero. - */ - if (shortLogCount < kShortLogMaxLines) { - shortLogNext = 0; - } else { - shortLogCount = kShortLogMaxLines; /* cap at window size */ - } - - for (i = 0; i < shortLogCount; i++) { - _LOG(tfd, true, "%s\n", shortLog[shortLogNext]); - shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; - } - } - - close(logfd); -} - -/* - * Dumps the logs generated by the specified pid to the tombstone, from both - * "system" and "main" log devices. Ideally we'd interleave the output. - */ -static void dump_logs(int tfd, unsigned pid, bool tailOnly) -{ - dump_log_file(tfd, pid, "/dev/log/system", tailOnly); - dump_log_file(tfd, pid, "/dev/log/main", tailOnly); -} - -/* Return true if some thread is not detached cleanly */ -static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid, - int signal) -{ - int fd; - bool need_cleanup = false; - - /* don't copy log messages to tombstone unless this is a dev device */ - char value[PROPERTY_VALUE_MAX]; - property_get("ro.debuggable", value, "0"); - bool wantLogs = (value[0] == '1'); - - mkdir(TOMBSTONE_DIR, 0755); - chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); - - fd = find_and_open_tombstone(); - if (fd < 0) - return need_cleanup; - - dump_crash_banner(fd, pid, tid, signal); - dump_crash_report(fd, pid, tid, true); - - if (wantLogs) { - dump_logs(fd, pid, true); - } - - /* - * If the user has requested to attach gdb, don't collect the per-thread - * information as it increases the chance to lose track of the process. - */ - if ((signed)pid > debug_uid) { - need_cleanup = dump_sibling_thread_report(fd, pid, tid); - } - - if (wantLogs) { - dump_logs(fd, pid, false); - } - - close(fd); - return need_cleanup; -} +typedef struct { + debugger_action_t action; + pid_t pid, tid; + uid_t uid, gid; +} debugger_request_t; static int write_string(const char* file, const char* string) @@ -654,25 +99,21 @@ void disable_debug_led(void) write_string("/sys/class/leds/left/cadence", "0,0"); } -extern int init_getevent(); -extern void uninit_getevent(); -extern int get_event(struct input_event* event, int timeout); - -static void wait_for_user_action(unsigned tid, struct ucred* cr) -{ - (void)tid; +static void wait_for_user_action(pid_t pid) { /* First log a helpful message */ LOG( "********************************************************\n" "* Process %d has been suspended while crashing. To\n" - "* attach gdbserver for a gdb connection on port 5039:\n" + "* attach gdbserver for a gdb connection on port 5039\n" + "* and start gdbclient:\n" "*\n" - "* adb shell gdbserver :5039 --attach %d &\n" + "* gdbclient app_process :5039 %d\n" "*\n" - "* Press HOME key to let the process continue crashing.\n" + "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" + "* to let the process continue crashing.\n" "********************************************************\n", - cr->pid, cr->pid); + pid, pid); - /* wait for HOME key (TODO: something useful for devices w/o HOME key) */ + /* wait for HOME or VOLUME DOWN key */ if (init_getevent() == 0) { int ms = 1200 / 10; int dit = 1; @@ -685,15 +126,18 @@ static void wait_for_user_action(unsigned tid, struct ucred* cr) }; size_t s = 0; struct input_event e; - int home = 0; + bool done = false; init_debug_led(); enable_debug_led(); do { int timeout = abs((int)(codes[s])) * ms; int res = get_event(&e, timeout); if (res == 0) { - if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0) - home = 1; + if (e.type == EV_KEY + && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) + && e.value == 0) { + done = true; + } } else if (res == 1) { if (++s >= sizeof(codes)/sizeof(*codes)) s = 0; @@ -703,202 +147,269 @@ static void wait_for_user_action(unsigned tid, struct ucred* cr) disable_debug_led(); } } - } while (!home); + } while (!done); uninit_getevent(); } /* don't forget to turn debug led off */ disable_debug_led(); + LOG("debuggerd resuming process %d", pid); +} - /* close filedescriptor */ - LOG("debuggerd resuming process %d", cr->pid); - } +static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/status", tid); + + FILE* fp = fopen(path, "r"); + if (!fp) { + return -1; + } + + int fields = 0; + char line[1024]; + while (fgets(line, sizeof(line), fp)) { + size_t len = strlen(line); + if (len > 6 && !memcmp(line, "Tgid:\t", 6)) { + *out_pid = atoi(line + 6); + fields |= 1; + } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) { + *out_uid = atoi(line + 5); + fields |= 2; + } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) { + *out_gid = atoi(line + 5); + fields |= 4; + } + } + fclose(fp); + return fields == 7 ? 0 : -1; +} -static void handle_crashing_process(int fd) -{ - char buf[64]; - struct stat s; - unsigned tid; +static int read_request(int fd, debugger_request_t* out_request) { struct ucred cr; - int n, len, status; - int tid_attach_status = -1; - unsigned retry = 30; - bool need_cleanup = false; - - char value[PROPERTY_VALUE_MAX]; - property_get("debug.db.uid", value, "-1"); - int debug_uid = atoi(value); - - XLOG("handle_crashing_process(%d)\n", fd); - - len = sizeof(cr); - n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); - if(n != 0) { + int len = sizeof(cr); + int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); + if (status != 0) { LOG("cannot get credentials\n"); - goto done; + return -1; } XLOG("reading tid\n"); fcntl(fd, F_SETFL, O_NONBLOCK); - while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) { - if(errno == EINTR) continue; - if(errno == EWOULDBLOCK) { - if(retry-- > 0) { - usleep(100 * 1000); - continue; - } - LOG("timed out reading tid\n"); - goto done; - } - LOG("read failure? %s\n", strerror(errno)); - goto done; - } - snprintf(buf, sizeof buf, "/proc/%d/task/%d", cr.pid, tid); - if(stat(buf, &s)) { - LOG("tid %d does not exist in pid %d. ignoring debug request\n", - tid, cr.pid); - close(fd); - return; + struct pollfd pollfds[1]; + pollfds[0].fd = fd; + pollfds[0].events = POLLIN; + pollfds[0].revents = 0; + status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); + if (status != 1) { + LOG("timed out reading tid\n"); + return -1; } - XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid); - - /* Note that at this point, the target thread's signal handler - * is blocked in a read() call. This gives us the time to PTRACE_ATTACH - * to it before it has a chance to really fault. - * - * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it - * won't necessarily have stopped by the time ptrace() returns. (We - * currently assume it does.) We write to the file descriptor to - * ensure that it can run as soon as we call PTRACE_CONT below. - * See details in bionic/libc/linker/debugger.c, in function - * debugger_signal_handler(). - */ - tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0); - int ptrace_error = errno; - - if (TEMP_FAILURE_RETRY(write(fd, &tid, 1)) != 1) { - XLOG("failed responding to client: %s\n", - strerror(errno)); - goto done; + debugger_msg_t msg; + status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); + if (status < 0) { + LOG("read failure? %s\n", strerror(errno)); + return -1; + } + if (status != sizeof(msg)) { + LOG("invalid crash request of size %d\n", status); + return -1; + } + + out_request->action = msg.action; + out_request->tid = msg.tid; + out_request->pid = cr.pid; + out_request->uid = cr.uid; + out_request->gid = cr.gid; + + if (msg.action == DEBUGGER_ACTION_CRASH) { + /* Ensure that the tid reported by the crashing process is valid. */ + char buf[64]; + struct stat s; + snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); + if(stat(buf, &s)) { + LOG("tid %d does not exist in pid %d. ignoring debug request\n", + out_request->tid, out_request->pid); + return -1; + } + } else if (cr.uid == 0 + || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { + /* Only root or system can ask us to attach to any process and dump it explicitly. + * However, system is only allowed to collect backtraces but cannot dump tombstones. */ + status = get_process_info(out_request->tid, &out_request->pid, + &out_request->uid, &out_request->gid); + if (status < 0) { + LOG("tid %d does not exist. ignoring explicit dump request\n", + out_request->tid); + return -1; + } + } else { + /* No one else is not allowed to dump arbitrary processes. */ + return -1; } + return 0; +} - if(tid_attach_status < 0) { - LOG("ptrace attach failed: %s\n", strerror(ptrace_error)); - goto done; +static bool should_attach_gdb(debugger_request_t* request) { + if (request->action == DEBUGGER_ACTION_CRASH) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.db.uid", value, "-1"); + int debug_uid = atoi(value); + return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; } + return false; +} - close(fd); - fd = -1; +static void handle_request(int fd) { + XLOG("handle_request(%d)\n", fd); - const int sleep_time_usec = 200000; /* 0.2 seconds */ - const int max_total_sleep_usec = 3000000; /* 3 seconds */ - int loop_limit = max_total_sleep_usec / sleep_time_usec; - for(;;) { - if (loop_limit-- == 0) { - LOG("timed out waiting for pid=%d tid=%d uid=%d to die\n", - cr.pid, tid, cr.uid); - goto done; - } - n = waitpid(tid, &status, __WALL | WNOHANG); + debugger_request_t request; + int status = read_request(fd, &request); + if (!status) { + XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", + request.pid, request.uid, request.gid, request.tid); - if (n == 0) { - /* not ready yet */ - XLOG("not ready yet\n"); - usleep(sleep_time_usec); - continue; - } + /* At this point, the thread that made the request is blocked in + * a read() call. If the thread has crashed, then this gives us + * time to PTRACE_ATTACH to it before it has a chance to really fault. + * + * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it + * won't necessarily have stopped by the time ptrace() returns. (We + * currently assume it does.) We write to the file descriptor to + * ensure that it can run as soon as we call PTRACE_CONT below. + * See details in bionic/libc/linker/debugger.c, in function + * debugger_signal_handler(). + */ + if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { + LOG("ptrace attach failed: %s\n", strerror(errno)); + } else { + bool detach_failed = false; + bool attach_gdb = should_attach_gdb(&request); + if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { + LOG("failed responding to client: %s\n", strerror(errno)); + } else { + char* tombstone_path = NULL; - if(n < 0) { - if(errno == EAGAIN) continue; - LOG("waitpid failed: %s\n", strerror(errno)); - goto done; - } + if (request.action == DEBUGGER_ACTION_CRASH) { + close(fd); + fd = -1; + } - XLOG("waitpid: n=%d status=%08x\n", n, status); - - if(WIFSTOPPED(status)){ - n = WSTOPSIG(status); - switch(n) { - case SIGSTOP: - XLOG("stopped -- continuing\n"); - n = ptrace(PTRACE_CONT, tid, 0, 0); - if(n) { - LOG("ptrace failed: %s\n", strerror(errno)); - goto done; + int total_sleep_time_usec = 0; + for (;;) { + int signal = wait_for_signal(request.tid, &total_sleep_time_usec); + if (signal < 0) { + break; + } + + switch (signal) { + case SIGSTOP: + if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { + XLOG("stopped -- dumping to tombstone\n"); + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, true, true, &detach_failed, + &total_sleep_time_usec); + } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { + XLOG("stopped -- dumping to fd\n"); + dump_backtrace(fd, request.pid, request.tid, &detach_failed, + &total_sleep_time_usec); + } else { + XLOG("stopped -- continuing\n"); + status = ptrace(PTRACE_CONT, request.tid, 0, 0); + if (status) { + LOG("ptrace continue failed: %s\n", strerror(errno)); + } + continue; /* loop again */ + } + break; + + case SIGILL: + case SIGABRT: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + case SIGPIPE: + case SIGSTKFLT: { + XLOG("stopped -- fatal signal\n"); + /* + * Send a SIGSTOP to the process to make all of + * the non-signaled threads stop moving. Without + * this we get a lot of "ptrace detach failed: + * No such process". + */ + kill(request.pid, SIGSTOP); + /* don't dump sibling threads when attaching to GDB because it + * makes the process less reliable, apparently... */ + tombstone_path = engrave_tombstone(request.pid, request.tid, + signal, !attach_gdb, false, &detach_failed, + &total_sleep_time_usec); + break; + } + + default: + XLOG("stopped -- unexpected signal\n"); + LOG("process stopped due to unexpected signal %d\n", signal); + break; + } + break; } - continue; - - case SIGILL: - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGSEGV: - case SIGSTKFLT: { - XLOG("stopped -- fatal signal\n"); - need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n); - kill(tid, SIGSTOP); - goto done; - } - default: - XLOG("stopped -- unexpected signal\n"); - goto done; + if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { + if (tombstone_path) { + write(fd, tombstone_path, strlen(tombstone_path)); + } + close(fd); + fd = -1; + } + free(tombstone_path); } - } else { - XLOG("unexpected waitpid response\n"); - goto done; - } - } - -done: - XLOG("detaching\n"); - /* stop the process so we can debug */ - kill(cr.pid, SIGSTOP); + XLOG("detaching\n"); + if (attach_gdb) { + /* stop the process so we can debug */ + kill(request.pid, SIGSTOP); - /* - * If a thread has been attached by ptrace, make sure it is detached - * successfully otherwise we will get a zombie. - */ - if (tid_attach_status == 0) { - int detach_status; - /* detach so we can attach gdbserver */ - detach_status = ptrace(PTRACE_DETACH, tid, 0, 0); - need_cleanup |= (detach_status != 0); - } + /* detach so we can attach gdbserver */ + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; + } - /* - * if debug.db.uid is set, its value indicates if we should wait - * for user action for the crashing process. - * in this case, we log a message and turn the debug LED on - * waiting for a gdb connection (for instance) - */ + /* + * if debug.db.uid is set, its value indicates if we should wait + * for user action for the crashing process. + * in this case, we log a message and turn the debug LED on + * waiting for a gdb connection (for instance) + */ + wait_for_user_action(request.pid); + } else { + /* just detach */ + if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { + LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); + detach_failed = true; + } + } - if ((signed)cr.uid <= debug_uid) { - wait_for_user_action(tid, &cr); - } + /* resume stopped process (so it can crash in peace). */ + kill(request.pid, SIGCONT); - /* - * Resume stopped process (so it can crash in peace). If we didn't - * successfully detach, we're still the parent, and the actual parent - * won't receive a death notification via wait(2). At this point - * there's not much we can do about that. - */ - kill(cr.pid, SIGCONT); + /* If we didn't successfully detach, we're still the parent, and the + * actual parent won't receive a death notification via wait(2). At this point + * there's not much we can do about that. */ + if (detach_failed) { + LOG("debuggerd committing suicide to free the zombie!\n"); + kill(getpid(), SIGKILL); + } + } - if (need_cleanup) { - LOG("debuggerd committing suicide to free the zombie!\n"); - kill(getpid(), SIGKILL); } - - if(fd != -1) close(fd); + if (fd >= 0) { + close(fd); + } } - -int main() -{ +static int do_server() { int s; struct sigaction act; int logsocket = -1; @@ -912,8 +423,8 @@ int main() signal(SIGBUS, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGSEGV, SIG_DFL); - signal(SIGSTKFLT, SIG_DFL); signal(SIGPIPE, SIG_DFL); + signal(SIGSTKFLT, SIG_DFL); logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); @@ -929,9 +440,9 @@ int main() act.sa_flags = SA_NOCLDWAIT; sigaction(SIGCHLD, &act, 0); - s = socket_local_server("android:debuggerd", + s = socket_local_server(DEBUGGER_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); - if(s < 0) return -1; + if(s < 0) return 1; fcntl(s, F_SETFD, FD_CLOEXEC); LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); @@ -951,7 +462,61 @@ int main() fcntl(fd, F_SETFD, FD_CLOEXEC); - handle_crashing_process(fd); + handle_request(fd); } return 0; } + +static int do_explicit_dump(pid_t tid, bool dump_backtrace) { + fprintf(stdout, "Sending request to dump task %d.\n", tid); + + if (dump_backtrace) { + fflush(stdout); + if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) { + fputs("Error dumping backtrace.\n", stderr); + return 1; + } + } else { + char tombstone_path[PATH_MAX]; + if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) { + fputs("Error dumping tombstone.\n", stderr); + return 1; + } + fprintf(stderr, "Tombstone written to: %s\n", tombstone_path); + } + return 0; +} + +static void usage() { + fputs("Usage: -b [<tid>]\n" + " -b dump backtrace to console, otherwise dump full tombstone file\n" + "\n" + "If tid specified, sends a request to debuggerd to dump that task.\n" + "Otherwise, starts the debuggerd server.\n", stderr); +} + +int main(int argc, char** argv) { + if (argc == 1) { + return do_server(); + } + + bool dump_backtrace = false; + bool have_tid = false; + pid_t tid = 0; + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-b")) { + dump_backtrace = true; + } else if (!have_tid) { + tid = atoi(argv[i]); + have_tid = true; + } else { + usage(); + return 1; + } + } + if (!have_tid) { + usage(); + return 1; + } + return do_explicit_dump(tid, dump_backtrace); +} diff --git a/debuggerd/debuggerd.h b/debuggerd/debuggerd.h deleted file mode 100644 index e3cdc7c..0000000 --- a/debuggerd/debuggerd.h +++ /dev/null @@ -1,39 +0,0 @@ -/* system/debuggerd/debuggerd.h -** -** Copyright 2006, 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 <cutils/logd.h> -#include <sys/ptrace.h> -#include <unwind.h> -#include "utility.h" -#include "symbol_table.h" - - -/* Main entry point to get the backtrace from the crashing process */ -extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map, - unsigned int sp_list[], - int *frame0_pc_sane, - bool at_fault); - -extern void dump_registers(int tfd, int pid, bool at_fault); - -extern int unwind_backtrace_with_ptrace_x86(int tfd, pid_t pid, mapinfo *map, bool at_fault); - -void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level, bool at_fault); - -void dump_stack_and_code(int tfd, int pid, mapinfo *map, - int unwind_depth, unsigned int sp_list[], - bool at_fault); diff --git a/nexus/IWifiStatusPollerHandler.h b/debuggerd/getevent.h index 9b94945..426139d 100644 --- a/nexus/IWifiStatusPollerHandler.h +++ b/debuggerd/getevent.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 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. @@ -14,13 +14,11 @@ * limitations under the License. */ -#ifndef _IWIFISTATUSPOLLER_HANDLER_H -#define _IWIFISTATUSPOLLER_HANDLER_H +#ifndef _DEBUGGERD_GETEVENT_H +#define _DEBUGGERD_GETEVENT_H -class IWifiStatusPollerHandler { -public: - virtual ~IWifiStatusPollerHandler() {} - virtual void onStatusPollInterval() = 0; -}; +int init_getevent(); +void uninit_getevent(); +int get_event(struct input_event* event, int timeout); -#endif +#endif // _DEBUGGERD_GETEVENT_H diff --git a/nexus/SupplicantAssociatedEvent.h b/debuggerd/machine.h index aa33c59..1619dd3 100644 --- a/nexus/SupplicantAssociatedEvent.h +++ b/debuggerd/machine.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 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. @@ -14,21 +14,18 @@ * limitations under the License. */ -#ifndef _SupplicantAssociatedEvent_H -#define _SupplicantAssociatedEvent_H +#ifndef _DEBUGGERD_MACHINE_H +#define _DEBUGGERD_MACHINE_H -#include "SupplicantEvent.h" +#include <stddef.h> +#include <stdbool.h> +#include <sys/types.h> -class SupplicantAssociatedEvent : public SupplicantEvent { - char *mBssid; - char *mSsid; - int mFreq; +#include <corkscrew/ptrace.h> -public: - SupplicantAssociatedEvent(int level, char *event, size_t len); - virtual ~SupplicantAssociatedEvent(); +#include "utility.h" - const char *getBssid() { return mBssid; } -}; +void dump_memory_and_code(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault); +void dump_registers(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault); -#endif +#endif // _DEBUGGERD_MACHINE_H diff --git a/debuggerd/symbol_table.c b/debuggerd/symbol_table.c deleted file mode 100644 index 23572a3..0000000 --- a/debuggerd/symbol_table.c +++ /dev/null @@ -1,240 +0,0 @@ -#include <stdlib.h> -#include <fcntl.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/mman.h> - -#include "symbol_table.h" -#include "utility.h" - -#include <linux/elf.h> - -// Compare func for qsort -static int qcompar(const void *a, const void *b) -{ - return ((struct symbol*)a)->addr - ((struct symbol*)b)->addr; -} - -// Compare func for bsearch -static int bcompar(const void *addr, const void *element) -{ - struct symbol *symbol = (struct symbol*)element; - - if((unsigned int)addr < symbol->addr) { - return -1; - } - - if((unsigned int)addr - symbol->addr >= symbol->size) { - return 1; - } - - return 0; -} - -/* - * Create a symbol table from a given file - * - * Parameters: - * filename - Filename to process - * - * Returns: - * A newly-allocated SymbolTable structure, or NULL if error. - * Free symbol table with symbol_table_free() - */ -struct symbol_table *symbol_table_create(const char *filename) -{ - struct symbol_table *table = NULL; - - // Open the file, and map it into memory - struct stat sb; - int length; - char *base; - - XLOG2("Creating symbol table for %s\n", filename); - int fd = open(filename, O_RDONLY); - - if(fd < 0) { - goto out; - } - - fstat(fd, &sb); - length = sb.st_size; - - base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); - - if(!base) { - goto out_close; - } - - // Parse the file header - Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; - Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); - - // Search for the dynamic symbols section - int sym_idx = -1; - int dynsym_idx = -1; - int i; - - for(i = 0; i < hdr->e_shnum; i++) { - if(shdr[i].sh_type == SHT_SYMTAB ) { - sym_idx = i; - } - if(shdr[i].sh_type == SHT_DYNSYM ) { - dynsym_idx = i; - } - } - if ((dynsym_idx == -1) && (sym_idx == -1)) { - goto out_unmap; - } - - table = malloc(sizeof(struct symbol_table)); - if(!table) { - goto out_unmap; - } - table->name = strdup(filename); - table->num_symbols = 0; - - Elf32_Sym *dynsyms = NULL; - Elf32_Sym *syms = NULL; - int dynnumsyms = 0; - int numsyms = 0; - char *dynstr = NULL; - char *str = NULL; - - if (dynsym_idx != -1) { - dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset); - dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize; - int dynstr_idx = shdr[dynsym_idx].sh_link; - dynstr = base + shdr[dynstr_idx].sh_offset; - } - - if (sym_idx != -1) { - syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset); - numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize; - int str_idx = shdr[sym_idx].sh_link; - str = base + shdr[str_idx].sh_offset; - } - - int symbol_count = 0; - int dynsymbol_count = 0; - - if (dynsym_idx != -1) { - // Iterate through the dynamic symbol table, and count how many symbols - // are actually defined - for(i = 0; i < dynnumsyms; i++) { - if(dynsyms[i].st_shndx != SHN_UNDEF) { - dynsymbol_count++; - } - } - XLOG2("Dynamic Symbol count: %d\n", dynsymbol_count); - } - - if (sym_idx != -1) { - // Iterate through the symbol table, and count how many symbols - // are actually defined - for(i = 0; i < numsyms; i++) { - if((syms[i].st_shndx != SHN_UNDEF) && - (strlen(str+syms[i].st_name)) && - (syms[i].st_value != 0) && (syms[i].st_size != 0)) { - symbol_count++; - } - } - XLOG2("Symbol count: %d\n", symbol_count); - } - - // Now, create an entry in our symbol table structure for each symbol... - table->num_symbols += symbol_count + dynsymbol_count; - table->symbols = malloc(table->num_symbols * sizeof(struct symbol)); - if(!table->symbols) { - free(table); - table = NULL; - goto out_unmap; - } - - - int j = 0; - if (dynsym_idx != -1) { - // ...and populate them - for(i = 0; i < dynnumsyms; i++) { - if(dynsyms[i].st_shndx != SHN_UNDEF) { - table->symbols[j].name = strdup(dynstr + dynsyms[i].st_name); - table->symbols[j].addr = dynsyms[i].st_value; - table->symbols[j].size = dynsyms[i].st_size; - XLOG2("name: %s, addr: %x, size: %x\n", - table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size); - j++; - } - } - } - - if (sym_idx != -1) { - // ...and populate them - for(i = 0; i < numsyms; i++) { - if((syms[i].st_shndx != SHN_UNDEF) && - (strlen(str+syms[i].st_name)) && - (syms[i].st_value != 0) && (syms[i].st_size != 0)) { - table->symbols[j].name = strdup(str + syms[i].st_name); - table->symbols[j].addr = syms[i].st_value; - table->symbols[j].size = syms[i].st_size; - XLOG2("name: %s, addr: %x, size: %x\n", - table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size); - j++; - } - } - } - - // Sort the symbol table entries, so they can be bsearched later - qsort(table->symbols, table->num_symbols, sizeof(struct symbol), qcompar); - -out_unmap: - munmap(base, length); - -out_close: - close(fd); - -out: - return table; -} - -/* - * Free a symbol table - * - * Parameters: - * table - Table to free - */ -void symbol_table_free(struct symbol_table *table) -{ - int i; - - if(!table) { - return; - } - - for(i=0; i<table->num_symbols; i++) { - free(table->symbols[i].name); - } - - free(table->symbols); - free(table); -} - -/* - * Search for an address in the symbol table - * - * Parameters: - * table - Table to search in - * addr - Address to search for. - * - * Returns: - * A pointer to the Symbol structure corresponding to the - * symbol which contains this address, or NULL if no symbol - * contains it. - */ -const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr) -{ - if(!table) { - return NULL; - } - - return bsearch((void*)addr, table->symbols, table->num_symbols, sizeof(struct symbol), bcompar); -} diff --git a/debuggerd/symbol_table.h b/debuggerd/symbol_table.h deleted file mode 100644 index 7f41f91..0000000 --- a/debuggerd/symbol_table.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SYMBOL_TABLE_H -#define SYMBOL_TABLE_H - -struct symbol { - unsigned int addr; - unsigned int size; - char *name; -}; - -struct symbol_table { - struct symbol *symbols; - int num_symbols; - char *name; -}; - -struct symbol_table *symbol_table_create(const char *filename); -void symbol_table_free(struct symbol_table *table); -const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr); - -#endif diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c new file mode 100644 index 0000000..27ab3fe --- /dev/null +++ b/debuggerd/tombstone.c @@ -0,0 +1,709 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <time.h> +#include <sys/ptrace.h> +#include <sys/stat.h> + +#include <private/android_filesystem_config.h> + +#include <cutils/logger.h> +#include <cutils/properties.h> + +#include <corkscrew/demangle.h> +#include <corkscrew/backtrace.h> + +#ifdef HAVE_SELINUX +#include <selinux/android.h> +#endif + +#include "machine.h" +#include "tombstone.h" +#include "utility.h" + +#define STACK_DEPTH 32 +#define STACK_WORDS 16 + +#define MAX_TOMBSTONES 10 +#define TOMBSTONE_DIR "/data/tombstones" + +#define typecheck(x,y) { \ + typeof(x) __dummy1; \ + typeof(y) __dummy2; \ + (void)(&__dummy1 == &__dummy2); } + + +static bool signal_has_address(int sig) { + switch (sig) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + return true; + default: + return false; + } +} + +static const char *get_signame(int sig) +{ + switch(sig) { + case SIGILL: return "SIGILL"; + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGSEGV: return "SIGSEGV"; + case SIGPIPE: return "SIGPIPE"; + case SIGSTKFLT: return "SIGSTKFLT"; + case SIGSTOP: return "SIGSTOP"; + default: return "?"; + } +} + +static const char *get_sigcode(int signo, int code) +{ + switch (signo) { + case SIGILL: + switch (code) { + case ILL_ILLOPC: return "ILL_ILLOPC"; + case ILL_ILLOPN: return "ILL_ILLOPN"; + case ILL_ILLADR: return "ILL_ILLADR"; + case ILL_ILLTRP: return "ILL_ILLTRP"; + case ILL_PRVOPC: return "ILL_PRVOPC"; + case ILL_PRVREG: return "ILL_PRVREG"; + case ILL_COPROC: return "ILL_COPROC"; + case ILL_BADSTK: return "ILL_BADSTK"; + } + break; + case SIGBUS: + switch (code) { + case BUS_ADRALN: return "BUS_ADRALN"; + case BUS_ADRERR: return "BUS_ADRERR"; + case BUS_OBJERR: return "BUS_OBJERR"; + } + break; + case SIGFPE: + switch (code) { + case FPE_INTDIV: return "FPE_INTDIV"; + case FPE_INTOVF: return "FPE_INTOVF"; + case FPE_FLTDIV: return "FPE_FLTDIV"; + case FPE_FLTOVF: return "FPE_FLTOVF"; + case FPE_FLTUND: return "FPE_FLTUND"; + case FPE_FLTRES: return "FPE_FLTRES"; + case FPE_FLTINV: return "FPE_FLTINV"; + case FPE_FLTSUB: return "FPE_FLTSUB"; + } + break; + case SIGSEGV: + switch (code) { + case SEGV_MAPERR: return "SEGV_MAPERR"; + case SEGV_ACCERR: return "SEGV_ACCERR"; + } + break; + } + return "?"; +} + +static void dump_build_info(log_t* log) +{ + char fingerprint[PROPERTY_VALUE_MAX]; + + property_get("ro.build.fingerprint", fingerprint, "unknown"); + + _LOG(log, false, "Build fingerprint: '%s'\n", fingerprint); +} + +static void dump_fault_addr(log_t* log, pid_t tid, int sig) +{ + siginfo_t si; + + memset(&si, 0, sizeof(si)); + if(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)){ + _LOG(log, false, "cannot get siginfo: %s\n", strerror(errno)); + } else if (signal_has_address(sig)) { + _LOG(log, false, "signal %d (%s), code %d (%s), fault addr %08x\n", + sig, get_signame(sig), + si.si_code, get_sigcode(sig, si.si_code), + (uintptr_t) si.si_addr); + } else { + _LOG(log, false, "signal %d (%s), code %d (%s), fault addr --------\n", + sig, get_signame(sig), si.si_code, get_sigcode(sig, si.si_code)); + } +} + +static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, bool at_fault) { + char path[64]; + char threadnamebuf[1024]; + char* threadname = NULL; + FILE *fp; + + snprintf(path, sizeof(path), "/proc/%d/comm", tid); + if ((fp = fopen(path, "r"))) { + threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); + fclose(fp); + if (threadname) { + size_t len = strlen(threadname); + if (len && threadname[len - 1] == '\n') { + threadname[len - 1] = '\0'; + } + } + } + + if (at_fault) { + char procnamebuf[1024]; + char* procname = NULL; + + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + if ((fp = fopen(path, "r"))) { + procname = fgets(procnamebuf, sizeof(procnamebuf), fp); + fclose(fp); + } + + _LOG(log, false, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, + threadname ? threadname : "UNKNOWN", + procname ? procname : "UNKNOWN"); + } else { + _LOG(log, true, "pid: %d, tid: %d, name: %s\n", pid, tid, + threadname ? threadname : "UNKNOWN"); + } +} + +static void dump_backtrace(const ptrace_context_t* context __attribute((unused)), + log_t* log, pid_t tid __attribute((unused)), bool at_fault, + const backtrace_frame_t* backtrace, size_t frames) { + _LOG(log, !at_fault, "\nbacktrace:\n"); + + backtrace_symbol_t backtrace_symbols[STACK_DEPTH]; + get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols); + for (size_t i = 0; i < frames; i++) { + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + _LOG(log, !at_fault, " %s\n", line); + } + free_backtrace_symbols(backtrace_symbols, frames); +} + +static void dump_stack_segment(const ptrace_context_t* context, log_t* log, pid_t tid, + bool only_in_tombstone, uintptr_t* sp, size_t words, int label) { + for (size_t i = 0; i < words; i++) { + uint32_t stack_content; + if (!try_get_word_ptrace(tid, *sp, &stack_content)) { + break; + } + + const map_info_t* mi; + const symbol_t* symbol; + find_symbol_ptrace(context, stack_content, &mi, &symbol); + + if (symbol) { + char* demangled_name = demangle_symbol_name(symbol->name); + const char* symbol_name = demangled_name ? demangled_name : symbol->name; + uint32_t offset = stack_content - (mi->start + symbol->start); + if (!i && label >= 0) { + if (offset) { + _LOG(log, only_in_tombstone, " #%02d %08x %08x %s (%s+%u)\n", + label, *sp, stack_content, mi ? mi->name : "", symbol_name, offset); + } else { + _LOG(log, only_in_tombstone, " #%02d %08x %08x %s (%s)\n", + label, *sp, stack_content, mi ? mi->name : "", symbol_name); + } + } else { + if (offset) { + _LOG(log, only_in_tombstone, " %08x %08x %s (%s+%u)\n", + *sp, stack_content, mi ? mi->name : "", symbol_name, offset); + } else { + _LOG(log, only_in_tombstone, " %08x %08x %s (%s)\n", + *sp, stack_content, mi ? mi->name : "", symbol_name); + } + } + free(demangled_name); + } else { + if (!i && label >= 0) { + _LOG(log, only_in_tombstone, " #%02d %08x %08x %s\n", + label, *sp, stack_content, mi ? mi->name : ""); + } else { + _LOG(log, only_in_tombstone, " %08x %08x %s\n", + *sp, stack_content, mi ? mi->name : ""); + } + } + + *sp += sizeof(uint32_t); + } +} + +static void dump_stack(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault, + const backtrace_frame_t* backtrace, size_t frames) { + bool have_first = false; + size_t first, last; + for (size_t i = 0; i < frames; i++) { + if (backtrace[i].stack_top) { + if (!have_first) { + have_first = true; + first = i; + } + last = i; + } + } + if (!have_first) { + return; + } + + _LOG(log, !at_fault, "\nstack:\n"); + + // Dump a few words before the first frame. + bool only_in_tombstone = !at_fault; + uintptr_t sp = backtrace[first].stack_top - STACK_WORDS * sizeof(uint32_t); + dump_stack_segment(context, log, tid, only_in_tombstone, &sp, STACK_WORDS, -1); + + // Dump a few words from all successive frames. + // Only log the first 3 frames, put the rest in the tombstone. + for (size_t i = first; i <= last; i++) { + const backtrace_frame_t* frame = &backtrace[i]; + if (sp != frame->stack_top) { + _LOG(log, only_in_tombstone, " ........ ........\n"); + sp = frame->stack_top; + } + if (i - first == 3) { + only_in_tombstone = true; + } + if (i == last) { + dump_stack_segment(context, log, tid, only_in_tombstone, &sp, STACK_WORDS, i); + if (sp < frame->stack_top + frame->stack_size) { + _LOG(log, only_in_tombstone, " ........ ........\n"); + } + } else { + size_t words = frame->stack_size / sizeof(uint32_t); + if (words == 0) { + words = 1; + } else if (words > STACK_WORDS) { + words = STACK_WORDS; + } + dump_stack_segment(context, log, tid, only_in_tombstone, &sp, words, i); + } + } +} + +static void dump_backtrace_and_stack(const ptrace_context_t* context, log_t* log, pid_t tid, + bool at_fault) { + backtrace_frame_t backtrace[STACK_DEPTH]; + ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH); + if (frames > 0) { + dump_backtrace(context, log, tid, at_fault, backtrace, frames); + dump_stack(context, log, tid, at_fault, backtrace, frames); + } +} + +static void dump_nearby_maps(const ptrace_context_t* context, log_t* log, pid_t tid) { + siginfo_t si; + memset(&si, 0, sizeof(si)); + if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) { + _LOG(log, false, "cannot get siginfo for %d: %s\n", + tid, strerror(errno)); + return; + } + if (!signal_has_address(si.si_signo)) { + return; + } + + uintptr_t addr = (uintptr_t) si.si_addr; + addr &= ~0xfff; /* round to 4K page boundary */ + if (addr == 0) { /* null-pointer deref */ + return; + } + + _LOG(log, false, "\nmemory map around fault addr %08x:\n", (int)si.si_addr); + + /* + * Search for a match, or for a hole where the match would be. The list + * is backward from the file content, so it starts at high addresses. + */ + map_info_t* map = context->map_info_list; + map_info_t *next = NULL; + map_info_t *prev = NULL; + while (map != NULL) { + if (addr >= map->start && addr < map->end) { + next = map->next; + break; + } else if (addr >= map->end) { + /* map would be between "prev" and this entry */ + next = map; + map = NULL; + break; + } + + prev = map; + map = map->next; + } + + /* + * Show "next" then "match" then "prev" so that the addresses appear in + * ascending order (like /proc/pid/maps). + */ + if (next != NULL) { + _LOG(log, false, " %08x-%08x %s\n", next->start, next->end, next->name); + } else { + _LOG(log, false, " (no map below)\n"); + } + if (map != NULL) { + _LOG(log, false, " %08x-%08x %s\n", map->start, map->end, map->name); + } else { + _LOG(log, false, " (no map for address)\n"); + } + if (prev != NULL) { + _LOG(log, false, " %08x-%08x %s\n", prev->start, prev->end, prev->name); + } else { + _LOG(log, false, " (no map above)\n"); + } +} + +static void dump_thread(const ptrace_context_t* context, log_t* log, pid_t tid, bool at_fault, + int* total_sleep_time_usec) { + wait_for_stop(tid, total_sleep_time_usec); + + dump_registers(context, log, tid, at_fault); + dump_backtrace_and_stack(context, log, tid, at_fault); + if (at_fault) { + dump_memory_and_code(context, log, tid, at_fault); + dump_nearby_maps(context, log, tid); + } +} + +/* Return true if some thread is not detached cleanly */ +static bool dump_sibling_thread_report(const ptrace_context_t* context, + log_t* log, pid_t pid, pid_t tid, int* total_sleep_time_usec) { + char task_path[64]; + snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); + + DIR* d = opendir(task_path); + /* Bail early if cannot open the task directory */ + if (d == NULL) { + XLOG("Cannot open /proc/%d/task\n", pid); + return false; + } + + bool detach_failed = false; + struct dirent debuf; + struct dirent *de; + while (!readdir_r(d, &debuf, &de) && de) { + /* Ignore "." and ".." */ + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { + continue; + } + + /* The main thread at fault has been handled individually */ + char* end; + pid_t new_tid = strtoul(de->d_name, &end, 10); + if (*end || new_tid == tid) { + continue; + } + + /* Skip this thread if cannot ptrace it */ + if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) { + continue; + } + + _LOG(log, true, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); + dump_thread_info(log, pid, new_tid, false); + dump_thread(context, log, new_tid, false, total_sleep_time_usec); + + if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) { + LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno)); + detach_failed = true; + } + } + + closedir(d); + return detach_failed; +} + +/* + * Reads the contents of the specified log device, filters out the entries + * that don't match the specified pid, and writes them to the tombstone file. + * + * If "tailOnly" is set, we only print the last few lines. + */ +static void dump_log_file(log_t* log, pid_t pid, const char* filename, + bool tailOnly) +{ + bool first = true; + + /* circular buffer, for "tailOnly" mode */ + const int kShortLogMaxLines = 5; + const int kShortLogLineLen = 256; + char shortLog[kShortLogMaxLines][kShortLogLineLen]; + int shortLogCount = 0; + int shortLogNext = 0; + + int logfd = open(filename, O_RDONLY | O_NONBLOCK); + if (logfd < 0) { + XLOG("Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1]; + struct logger_entry entry; + } log_entry; + + while (true) { + ssize_t actual = read(logfd, log_entry.buf, LOGGER_ENTRY_MAX_LEN); + if (actual < 0) { + if (errno == EINTR) { + /* interrupted by signal, retry */ + continue; + } else if (errno == EAGAIN) { + /* non-blocking EOF; we're done */ + break; + } else { + _LOG(log, true, "Error while reading log: %s\n", + strerror(errno)); + break; + } + } else if (actual == 0) { + _LOG(log, true, "Got zero bytes while reading log: %s\n", + strerror(errno)); + break; + } + + /* + * NOTE: if you XLOG something here, this will spin forever, + * because you will be writing as fast as you're reading. Any + * high-frequency debug diagnostics should just be written to + * the tombstone file. + */ + + struct logger_entry* entry = &log_entry.entry; + + if (entry->pid != (int32_t) pid) { + /* wrong pid, ignore */ + continue; + } + + if (first) { + _LOG(log, true, "--------- %slog %s\n", + tailOnly ? "tail end of " : "", filename); + first = false; + } + + /* + * Msg format is: <priority:1><tag:N>\0<message:N>\0 + * + * We want to display it in the same format as "logcat -v threadtime" + * (although in this case the pid is redundant). + * + * TODO: scan for line breaks ('\n') and display each text line + * on a separate line, prefixed with the header, like logcat does. + */ + static const char* kPrioChars = "!.VDIWEFS"; + unsigned char prio = entry->msg[0]; + char* tag = entry->msg + 1; + char* msg = tag + strlen(tag) + 1; + + /* consume any trailing newlines */ + char* eatnl = msg + strlen(msg) - 1; + while (eatnl >= msg && *eatnl == '\n') { + *eatnl-- = '\0'; + } + + char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?'); + + char timeBuf[32]; + time_t sec = (time_t) entry->sec; + struct tm tmBuf; + struct tm* ptm; + ptm = localtime_r(&sec, &tmBuf); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + if (tailOnly) { + snprintf(shortLog[shortLogNext], kShortLogLineLen, + "%s.%03d %5d %5d %c %-8s: %s", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); + shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; + shortLogCount++; + } else { + _LOG(log, true, "%s.%03d %5d %5d %c %-8s: %s\n", + timeBuf, entry->nsec / 1000000, entry->pid, entry->tid, + prioChar, tag, msg); + } + } + + if (tailOnly) { + int i; + + /* + * If we filled the buffer, we want to start at "next", which has + * the oldest entry. If we didn't, we want to start at zero. + */ + if (shortLogCount < kShortLogMaxLines) { + shortLogNext = 0; + } else { + shortLogCount = kShortLogMaxLines; /* cap at window size */ + } + + for (i = 0; i < shortLogCount; i++) { + _LOG(log, true, "%s\n", shortLog[shortLogNext]); + shortLogNext = (shortLogNext + 1) % kShortLogMaxLines; + } + } + + close(logfd); +} + +/* + * Dumps the logs generated by the specified pid to the tombstone, from both + * "system" and "main" log devices. Ideally we'd interleave the output. + */ +static void dump_logs(log_t* log, pid_t pid, bool tailOnly) +{ + dump_log_file(log, pid, "/dev/log/system", tailOnly); + dump_log_file(log, pid, "/dev/log/main", tailOnly); +} + +/* + * Dumps all information about the specified pid to the tombstone. + */ +static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, + bool dump_sibling_threads, int* total_sleep_time_usec) +{ + /* don't copy log messages to tombstone unless this is a dev device */ + char value[PROPERTY_VALUE_MAX]; + property_get("ro.debuggable", value, "0"); + bool want_logs = (value[0] == '1'); + + _LOG(log, false, + "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); + dump_build_info(log); + dump_thread_info(log, pid, tid, true); + if(signal) { + dump_fault_addr(log, tid, signal); + } + + ptrace_context_t* context = load_ptrace_context(tid); + dump_thread(context, log, tid, true, total_sleep_time_usec); + + if (want_logs) { + dump_logs(log, pid, true); + } + + bool detach_failed = false; + if (dump_sibling_threads) { + detach_failed = dump_sibling_thread_report(context, log, pid, tid, total_sleep_time_usec); + } + + free_ptrace_context(context); + + if (want_logs) { + dump_logs(log, pid, false); + } + return detach_failed; +} + +/* + * find_and_open_tombstone - find an available tombstone slot, if any, of the + * form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no + * file is available, we reuse the least-recently-modified file. + * + * Returns the path of the tombstone file, allocated using malloc(). Caller must free() it. + */ +static char* find_and_open_tombstone(int* fd) +{ + unsigned long mtime = ULONG_MAX; + struct stat sb; + + /* + * XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought + * to, our logic breaks. This check will generate a warning if that happens. + */ + typecheck(mtime, sb.st_mtime); + + /* + * In a single wolf-like pass, find an available slot and, in case none + * exist, find and record the least-recently-modified file. + */ + char path[128]; + int oldest = 0; + for (int i = 0; i < MAX_TOMBSTONES; i++) { + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i); + + if (!stat(path, &sb)) { + if (sb.st_mtime < mtime) { + oldest = i; + mtime = sb.st_mtime; + } + continue; + } + if (errno != ENOENT) + continue; + + *fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600); + if (*fd < 0) + continue; /* raced ? */ + + fchown(*fd, AID_SYSTEM, AID_SYSTEM); + return strdup(path); + } + + /* we didn't find an available file, so we clobber the oldest one */ + snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest); + *fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (*fd < 0) { + LOG("failed to open tombstone file '%s': %s\n", path, strerror(errno)); + return NULL; + } + fchown(*fd, AID_SYSTEM, AID_SYSTEM); + return strdup(path); +} + +char* engrave_tombstone(pid_t pid, pid_t tid, int signal, + bool dump_sibling_threads, bool quiet, bool* detach_failed, + int* total_sleep_time_usec) { + mkdir(TOMBSTONE_DIR, 0755); + chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM); + +#ifdef HAVE_SELINUX + if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) { + *detach_failed = false; + return NULL; + } +#endif + + int fd; + char* path = find_and_open_tombstone(&fd); + if (!path) { + *detach_failed = false; + return NULL; + } + + log_t log; + log.tfd = fd; + log.quiet = quiet; + *detach_failed = dump_crash(&log, pid, tid, signal, dump_sibling_threads, + total_sleep_time_usec); + + close(fd); + return path; +} diff --git a/nexus/DhcpListener.h b/debuggerd/tombstone.h index ca6fe37..edcd7b1 100644 --- a/nexus/DhcpListener.h +++ b/debuggerd/tombstone.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,25 +14,18 @@ * limitations under the License. */ -#ifndef _DhcpListener_H -#define _DhcpListener_H +#ifndef _DEBUGGERD_TOMBSTONE_H +#define _DEBUGGERD_TOMBSTONE_H -#include <sysutils/SocketListener.h> +#include <stddef.h> +#include <stdbool.h> +#include <sys/types.h> -class IDhcpEventHandlers; -class Controller; +#include <corkscrew/ptrace.h> -class DhcpListener : public SocketListener { - IDhcpEventHandlers *mHandlers; - Controller *mController; +/* Creates a tombstone file and writes the crash dump to it. + * Returns the path of the tombstone, which must be freed using free(). */ +char* engrave_tombstone(pid_t pid, pid_t tid, int signal, + bool dump_sibling_threads, bool quiet, bool* detach_failed, int* total_sleep_time_usec); -public: - - DhcpListener(Controller *c, int socket, IDhcpEventHandlers *handlers); - virtual ~DhcpListener(); - -private: - bool onDataAvailable(SocketClient *cli); -}; - -#endif +#endif // _DEBUGGERD_TOMBSTONE_H diff --git a/debuggerd/utility.c b/debuggerd/utility.c index 409209c..aabaf74 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -15,88 +15,80 @@ ** limitations under the License. */ -#include <sys/ptrace.h> -#include <sys/exec_elf.h> -#include <signal.h> -#include <assert.h> +#include <stddef.h> +#include <stdbool.h> +#include <stdio.h> #include <string.h> #include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <cutils/logd.h> +#include <sys/ptrace.h> +#include <sys/wait.h> #include "utility.h" -/* Get a word from pid using ptrace. The result is the return value. */ -int get_remote_word(int pid, void *src) -{ - return ptrace(PTRACE_PEEKTEXT, pid, src, NULL); -} +const int sleep_time_usec = 50000; /* 0.05 seconds */ +const int max_total_sleep_usec = 10000000; /* 10 seconds */ +void _LOG(log_t* log, bool in_tombstone_only, const char *fmt, ...) { + char buf[512]; -/* Handy routine to read aggregated data from pid using ptrace. The read - * values are written to the dest locations directly. - */ -void get_remote_struct(int pid, void *src, void *dst, size_t size) -{ - unsigned int i; + va_list ap; + va_start(ap, fmt); - for (i = 0; i+4 <= size; i+=4) { - *(int *)((char *)dst+i) = ptrace(PTRACE_PEEKTEXT, pid, (char *)src+i, NULL); + if (log && log->tfd >= 0) { + int len; + vsnprintf(buf, sizeof(buf), fmt, ap); + len = strlen(buf); + write(log->tfd, buf, len); } - if (i < size) { - int val; - - assert((size - i) < 4); - val = ptrace(PTRACE_PEEKTEXT, pid, (char *)src+i, NULL); - while (i < size) { - ((unsigned char *)dst)[i] = val & 0xff; - i++; - val >>= 8; - } + if (!in_tombstone_only && (!log || !log->quiet)) { + __android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap); } + va_end(ap); } -/* Map a pc address to the name of the containing ELF file */ -const char *map_to_name(mapinfo *mi, unsigned pc, const char* def) -{ - while(mi) { - if((pc >= mi->start) && (pc < mi->end)){ - return mi->name; +int wait_for_signal(pid_t tid, int* total_sleep_time_usec) { + for (;;) { + int status; + pid_t n = waitpid(tid, &status, __WALL | WNOHANG); + if (n < 0) { + if(errno == EAGAIN) continue; + LOG("waitpid failed: %s\n", strerror(errno)); + return -1; + } else if (n > 0) { + XLOG("waitpid: n=%d status=%08x\n", n, status); + if (WIFSTOPPED(status)) { + return WSTOPSIG(status); + } else { + LOG("unexpected waitpid response: n=%d, status=%08x\n", n, status); + return -1; + } } - mi = mi->next; - } - return def; -} -/* Find the containing map info for the pc */ -const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc) -{ - *rel_pc = pc; - while(mi) { - if((pc >= mi->start) && (pc < mi->end)){ - // Only calculate the relative offset for shared libraries - if (strstr(mi->name, ".so")) { - *rel_pc -= mi->start; - } - return mi; + if (*total_sleep_time_usec > max_total_sleep_usec) { + LOG("timed out waiting for tid=%d to die\n", tid); + return -1; } - mi = mi->next; + + /* not ready yet */ + XLOG("not ready yet\n"); + usleep(sleep_time_usec); + *total_sleep_time_usec += sleep_time_usec; } - return NULL; } -/* - * Returns true if the specified signal has an associated address (i.e. it - * sets siginfo_t.si_addr). - */ -bool signal_has_address(int sig) -{ - switch (sig) { - case SIGILL: - case SIGFPE: - case SIGSEGV: - case SIGBUS: - return true; - default: - return false; +void wait_for_stop(pid_t tid, int* total_sleep_time_usec) { + siginfo_t si; + while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, tid, 0, &si)) < 0 && errno == ESRCH) { + if (*total_sleep_time_usec > max_total_sleep_usec) { + LOG("timed out waiting for tid=%d to stop\n", tid); + break; + } + + usleep(sleep_time_usec); + *total_sleep_time_usec += sleep_time_usec; } } diff --git a/debuggerd/utility.h b/debuggerd/utility.h index 4a935d2..136f46d 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -15,65 +15,40 @@ ** limitations under the License. */ -#ifndef __utility_h -#define __utility_h +#ifndef _DEBUGGERD_UTILITY_H +#define _DEBUGGERD_UTILITY_H #include <stddef.h> #include <stdbool.h> -#include "symbol_table.h" +typedef struct { + /* tombstone file descriptor */ + int tfd; + /* if true, does not log anything to the Android logcat */ + bool quiet; +} log_t; -#ifndef PT_ARM_EXIDX -#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ -#endif - -#define STACK_CONTENT_DEPTH 32 - -typedef struct mapinfo { - struct mapinfo *next; - unsigned start; - unsigned end; - unsigned exidx_start; - unsigned exidx_end; - struct symbol_table *symbols; - bool isExecutable; - char name[]; -} mapinfo; - -/* Get a word from pid using ptrace. The result is the return value. */ -extern int get_remote_word(int pid, void *src); - -/* Handy routine to read aggregated data from pid using ptrace. The read - * values are written to the dest locations directly. - */ -extern void get_remote_struct(int pid, void *src, void *dst, size_t size); - -/* Find the containing map for the pc */ -const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc, unsigned *rel_pc); +/* Log information onto the tombstone. */ +void _LOG(log_t* log, bool in_tombstone_only, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); -/* Map a pc address to the name of the containing ELF file */ -const char *map_to_name(mapinfo *mi, unsigned pc, const char* def); - -/* Log information onto the tombstone */ -extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...); - -/* Determine whether si_addr is valid for this signal */ -bool signal_has_address(int sig); - -#define LOG(fmt...) _LOG(-1, 0, fmt) +#define LOG(fmt...) _LOG(NULL, 0, fmt) /* Set to 1 for normal debug traces */ #if 0 -#define XLOG(fmt...) _LOG(-1, 0, fmt) +#define XLOG(fmt...) _LOG(NULL, 0, fmt) #else #define XLOG(fmt...) do {} while(0) #endif /* Set to 1 for chatty debug traces. Includes all resolved dynamic symbols */ #if 0 -#define XLOG2(fmt...) _LOG(-1, 0, fmt) +#define XLOG2(fmt...) _LOG(NULL, 0, fmt) #else #define XLOG2(fmt...) do {} while(0) #endif -#endif +int wait_for_signal(pid_t tid, int* total_sleep_time_usec); +void wait_for_stop(pid_t tid, int* total_sleep_time_usec); + +#endif // _DEBUGGERD_UTILITY_H diff --git a/debuggerd/x86/machine.c b/debuggerd/x86/machine.c index 9d418cf..01da5fe 100644 --- a/debuggerd/x86/machine.c +++ b/debuggerd/x86/machine.c @@ -15,47 +15,44 @@ ** limitations under the License. */ +#include <stddef.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> #include <stdio.h> #include <errno.h> -#include <signal.h> -#include <pthread.h> -#include <fcntl.h> #include <sys/types.h> -#include <dirent.h> - #include <sys/ptrace.h> -#include <sys/wait.h> -#include <sys/exec_elf.h> -#include <sys/stat.h> -#include <cutils/sockets.h> -#include <cutils/properties.h> +#include <corkscrew/ptrace.h> -#include <linux/input.h> +#include <linux/user.h> #include "../utility.h" -#include "x86_utility.h" +#include "../machine.h" + +void dump_memory_and_code(const ptrace_context_t* context __attribute((unused)), + log_t* log, pid_t tid, bool at_fault) { +} -void dump_registers(int tfd, int pid, bool at_fault) -{ +void dump_registers(const ptrace_context_t* context __attribute((unused)), + log_t* log, pid_t tid, bool at_fault) { struct pt_regs_x86 r; bool only_in_tombstone = !at_fault; - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) { - _LOG(tfd, only_in_tombstone, - "cannot get registers: %s\n", strerror(errno)); + if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { + _LOG(log, only_in_tombstone, "cannot get registers: %s\n", strerror(errno)); return; } -//if there is no stack, no print just like arm + //if there is no stack, no print just like arm if(!r.ebp) return; - _LOG(tfd, only_in_tombstone, " eax %08x ebx %08x ecx %08x edx %08x\n", + _LOG(log, only_in_tombstone, " eax %08x ebx %08x ecx %08x edx %08x\n", r.eax, r.ebx, r.ecx, r.edx); - _LOG(tfd, only_in_tombstone, " esi %08x edi %08x\n", + _LOG(log, only_in_tombstone, " esi %08x edi %08x\n", r.esi, r.edi); - _LOG(tfd, only_in_tombstone, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", + _LOG(log, only_in_tombstone, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", r.xcs, r.xds, r.xes, r.xfs, r.xss); - _LOG(tfd, only_in_tombstone, - " eip %08x ebp %08x esp %08x flags %08x\n", + _LOG(log, only_in_tombstone, " eip %08x ebp %08x esp %08x flags %08x\n", r.eip, r.ebp, r.esp, r.eflags); } diff --git a/debuggerd/x86/unwind.c b/debuggerd/x86/unwind.c deleted file mode 100644 index 0a7f04c..0000000 --- a/debuggerd/x86/unwind.c +++ /dev/null @@ -1,86 +0,0 @@ -#include <cutils/logd.h> -#include <sys/ptrace.h> -#include "../utility.h" -#include "x86_utility.h" - - -int unwind_backtrace_with_ptrace_x86(int tfd, pid_t pid, mapinfo *map, - bool at_fault) -{ - struct pt_regs_x86 r; - unsigned int stack_level = 0; - unsigned int stack_depth = 0; - unsigned int rel_pc; - unsigned int stack_ptr; - unsigned int stack_content; - - if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0; - unsigned int eip = (unsigned int)r.eip; - unsigned int ebp = (unsigned int)r.ebp; - unsigned int cur_sp = (unsigned int)r.esp; - const mapinfo *mi; - const struct symbol* sym = 0; - - -//ebp==0, it indicates that the stack is poped to the bottom or there is no stack at all. - while (ebp) { - mi = pc_to_mapinfo(map, eip, &rel_pc); - - /* See if we can determine what symbol this stack frame resides in */ - if (mi != 0 && mi->symbols != 0) { - sym = symbol_table_lookup(mi->symbols, rel_pc); - } - if (sym) { - _LOG(tfd, !at_fault, " #%02d eip: %08x %s (%s)\n", - stack_level, eip, mi ? mi->name : "", sym->name); - } else { - _LOG(tfd, !at_fault, " #%02d eip: %08x %s\n", - stack_level, eip, mi ? mi->name : ""); - } - - stack_level++; - if (stack_level >= STACK_DEPTH || eip == 0) - break; - eip = ptrace(PTRACE_PEEKTEXT, pid, (void*)(ebp + 4), NULL); - ebp = ptrace(PTRACE_PEEKTEXT, pid, (void*)ebp, NULL); - } - ebp = (unsigned int)r.ebp; - stack_depth = stack_level; - stack_level = 0; - if (ebp) - _LOG(tfd, !at_fault, "stack: \n"); - while (ebp) { - stack_ptr = cur_sp; - while((int)(ebp - stack_ptr) >= 0) { - stack_content = ptrace(PTRACE_PEEKTEXT, pid, (void*)stack_ptr, NULL); - mi = pc_to_mapinfo(map, stack_content, &rel_pc); - - /* See if we can determine what symbol this stack frame resides in */ - if (mi != 0 && mi->symbols != 0) { - sym = symbol_table_lookup(mi->symbols, rel_pc); - } - if (sym) { - _LOG(tfd, !at_fault, " #%02d %08x %08x %s (%s)\n", - stack_level, stack_ptr, stack_content, mi ? mi->name : "", sym->name); - } else { - _LOG(tfd, !at_fault, " #%02d %08x %08x %s\n", - stack_level, stack_ptr, stack_content, mi ? mi->name : ""); - } - - stack_ptr = stack_ptr + 4; - //the stack frame may be very deep. - if((int)(stack_ptr - cur_sp) >= STACK_FRAME_DEPTH) { - _LOG(tfd, !at_fault, " ...... ...... \n"); - break; - } - } - cur_sp = ebp + 4; - stack_level++; - if (stack_level >= STACK_DEPTH || stack_level >= stack_depth) - break; - ebp = ptrace(PTRACE_PEEKTEXT, pid, (void*)ebp, NULL); - } - - return stack_depth; -} - diff --git a/debuggerd/x86/x86_utility.h b/debuggerd/x86/x86_utility.h deleted file mode 100644 index ac6a885..0000000 --- a/debuggerd/x86/x86_utility.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -** -** Copyright 2006, 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. -*/ - -#define STACK_DEPTH 8 -#define STACK_FRAME_DEPTH 64 - -typedef struct pt_regs_x86 { - long ebx; - long ecx; - long edx; - long esi; - long edi; - long ebp; - long eax; - int xds; - int xes; - int xfs; - int xgs; - long orig_eax; - long eip; - int xcs; - long eflags; - long esp; - int xss; -}pt_regs_x86; - diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 3c5538f..e3261a7 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -16,8 +16,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg -LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ + $(LOCAL_PATH)/../../extras/ext4_utils +LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c LOCAL_MODULE := fastboot ifeq ($(HOST_OS),linux) @@ -47,7 +48,13 @@ ifeq ($(HOST_OS),windows) LOCAL_C_INCLUDES += development/host/windows/usb/api endif -LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz +LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz libext4_utils libsparse libz + +ifneq ($(HOST_OS),windows) +ifeq ($(HAVE_SELINUX), true) +LOCAL_STATIC_LIBRARIES += libselinux +endif # HAVE_SELINUX +endif # HOST_OS != windows include $(BUILD_HOST_EXECUTABLE) $(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) diff --git a/fastboot/bootimg.c b/fastboot/bootimg.c index e5aea4e..9e0e45c 100644 --- a/fastboot/bootimg.c +++ b/fastboot/bootimg.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -48,23 +48,23 @@ boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned second_actual; unsigned page_mask; boot_img_hdr *hdr; - + page_mask = page_size - 1; - + kernel_actual = (kernel_size + page_mask) & (~page_mask); ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask); second_actual = (second_size + page_mask) & (~page_mask); - + *bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual; - + hdr = calloc(*bootimg_size, 1); - + if(hdr == 0) { return hdr; } memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); - + hdr->kernel_size = kernel_size; hdr->ramdisk_size = ramdisk_size; hdr->second_size = second_size; @@ -74,9 +74,9 @@ boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, hdr->tags_addr = base + 0x00000100; hdr->page_size = page_size; - memcpy(hdr->magic + page_size, + memcpy(hdr->magic + page_size, kernel, kernel_size); - memcpy(hdr->magic + page_size + kernel_actual, + memcpy(hdr->magic + page_size + kernel_actual, ramdisk, ramdisk_size); memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual, second, second_size); diff --git a/fastboot/engine.c b/fastboot/engine.c index 6d94035..93be3de 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,20 +19,35 @@ * 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 + * 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 "fastboot.h" +#include "make_ext4fs.h" + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> +#include <stdbool.h> #include <string.h> +#include <sys/stat.h> #include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> -#include "fastboot.h" +#ifdef USE_MINGW +#include <fcntl.h> +#else +#include <sys/mman.h> +#endif + +extern struct fs_info info; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) double now() { @@ -50,7 +65,7 @@ char *mkmsg(const char *fmt, ...) va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); - + s = strdup(buf); if (s == 0) die("out of memory"); return s; @@ -60,15 +75,19 @@ char *mkmsg(const char *fmt, ...) #define OP_COMMAND 2 #define OP_QUERY 3 #define OP_NOTICE 4 +#define OP_FORMAT 5 +#define OP_DOWNLOAD_SPARSE 6 typedef struct Action Action; -struct Action +#define CMD_SIZE 64 + +struct Action { unsigned op; Action *next; - char cmd[64]; + char cmd[CMD_SIZE]; const char *prod; void *data; unsigned size; @@ -82,6 +101,49 @@ struct Action static Action *action_list = 0; static Action *action_last = 0; + +struct image_data { + long long partition_size; + long long image_size; // real size of image file + void *buffer; +}; + +void generate_ext4_image(struct image_data *image); +void cleanup_image(struct image_data *image); + +int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...) +{ + char cmd[CMD_SIZE] = "getvar:"; + int getvar_len = strlen(cmd); + va_list args; + + response[FB_RESPONSE_SZ] = '\0'; + va_start(args, fmt); + vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args); + va_end(args); + cmd[CMD_SIZE - 1] = '\0'; + return fb_command_response(usb, cmd, response); +} + +struct generator { + char *fs_type; + + /* generate image and return it as image->buffer. + * size of the buffer returned as image->image_size. + * + * image->partition_size specifies what is the size of the + * file partition we generate image for. + */ + void (*generate)(struct image_data *image); + + /* it cleans the buffer allocated during image creation. + * this function probably does free() or munmap(). + */ + void (*cleanup)(struct image_data *image); +} generators[] = { + { "ext4", generate_ext4_image, cleanup_image } +}; + static int cb_default(Action *a, int status, char *resp) { if (status) { @@ -133,6 +195,180 @@ void fb_queue_erase(const char *ptn) a->msg = mkmsg("erasing '%s'", ptn); } +/* Loads file content into buffer. Returns NULL on error. */ +static void *load_buffer(int fd, off_t size) +{ + void *buffer; + +#ifdef USE_MINGW + ssize_t count = 0; + + // mmap is more efficient but mingw does not support it. + // In this case we read whole image into memory buffer. + buffer = malloc(size); + if (!buffer) { + perror("malloc"); + return NULL; + } + + lseek(fd, 0, SEEK_SET); + while(count < size) { + ssize_t actually_read = read(fd, (char*)buffer+count, size-count); + + if (actually_read == 0) { + break; + } + if (actually_read < 0) { + if (errno == EINTR) { + continue; + } + perror("read"); + free(buffer); + return NULL; + } + + count += actually_read; + } +#else + buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + perror("mmap"); + return NULL; + } +#endif + + return buffer; +} + +void cleanup_image(struct image_data *image) +{ +#ifdef USE_MINGW + free(image->buffer); +#else + munmap(image->buffer, image->image_size); +#endif +} + +void generate_ext4_image(struct image_data *image) +{ + int fd; + struct stat st; + +#ifdef USE_MINGW + /* Ideally we should use tmpfile() here, the same as with unix version. + * But unfortunately it is not portable as it is not clear whether this + * function opens file in TEXT or BINARY mode. + * + * There are also some reports it is buggy: + * http://pdplab.it.uom.gr/teaching/gcc_manuals/gnulib.html#tmpfile + * http://www.mega-nerd.com/erikd/Blog/Windiots/tmpfile.html + */ + char *filename = tempnam(getenv("TEMP"), "fastboot-format.img"); + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644); + unlink(filename); +#else + fd = fileno(tmpfile()); +#endif + /* reset ext4fs info so we can be called multiple times */ + reset_ext4fs_info(); + info.len = image->partition_size; + make_ext4fs_internal(fd, NULL, NULL, NULL, 0, 1, 0, 0, 0, NULL); + + fstat(fd, &st); + image->image_size = st.st_size; + image->buffer = load_buffer(fd, st.st_size); + + close(fd); +} + +int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) +{ + const char *partition = a->cmd; + char response[FB_RESPONSE_SZ+1]; + int status = 0; + struct image_data image; + struct generator *generator = NULL; + int fd; + unsigned i; + char cmd[CMD_SIZE]; + + status = fb_getvar(usb, response, "partition-type:%s", partition); + if (status) { + if (skip_if_not_supported) { + fprintf(stderr, + "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, + "Can't determine partition type.\n"); + return 0; + } + fprintf(stderr,"FAILED (%s)\n", fb_get_error()); + return status; + } + + for (i = 0; i < ARRAY_SIZE(generators); i++) { + if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { + generator = &generators[i]; + break; + } + } + if (!generator) { + if (skip_if_not_supported) { + fprintf(stderr, + "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, + "File system type %s not supported.\n", response); + return 0; + } + fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n", + response); + return -1; + } + + status = fb_getvar(usb, response, "partition-size:%s", partition); + if (status) { + if (skip_if_not_supported) { + fprintf(stderr, + "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, "Unable to get partition size\n."); + return 0; + } + fprintf(stderr,"FAILED (%s)\n", fb_get_error()); + return status; + } + image.partition_size = strtoll(response, (char **)NULL, 16); + + generator->generate(&image); + if (!image.buffer) { + fprintf(stderr,"Cannot generate image.\n"); + return -1; + } + + // Following piece of code is similar to fb_queue_flash() but executes + // actions directly without queuing + fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024); + status = fb_download_data(usb, image.buffer, image.image_size); + if (status) goto cleanup; + + fprintf(stderr, "writing '%s'...\n", partition); + snprintf(cmd, sizeof(cmd), "flash:%s", partition); + status = fb_command(usb, cmd); + if (status) goto cleanup; + +cleanup: + generator->cleanup(&image); + + return status; +} + +void fb_queue_format(const char *partition, int skip_if_not_supported) +{ + Action *a; + + a = queue_action(OP_FORMAT, partition); + a->data = (void*)skip_if_not_supported; + a->msg = mkmsg("formatting '%s' partition", partition); +} + void fb_queue_flash(const char *ptn, void *data, unsigned sz) { Action *a; @@ -146,6 +382,19 @@ void fb_queue_flash(const char *ptn, void *data, unsigned sz) a->msg = mkmsg("writing '%s'", ptn); } +void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz) +{ + Action *a; + + a = queue_action(OP_DOWNLOAD_SPARSE, ""); + a->data = s; + a->size = 0; + a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024); + + a = queue_action(OP_COMMAND, "flash:%s", ptn); + a->msg = mkmsg("writing '%s'", ptn); +} + static int match(char *str, const char **value, unsigned count) { const char *val; @@ -340,6 +589,14 @@ int fb_execute_queue(usb_handle *usb) if (status) break; } else if (a->op == OP_NOTICE) { fprintf(stderr,"%s\n",(char*)a->data); + } else if (a->op == OP_FORMAT) { + status = fb_format(a, usb, (int)a->data); + status = a->func(a, status, status ? fb_get_error() : ""); + if (status) break; + } else if (a->op == OP_DOWNLOAD_SPARSE) { + status = fb_download_data_sparse(usb, a->data); + status = a->func(a, status, status ? fb_get_error() : ""); + if (status) break; } else { die("bogus action"); } diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index fb7ac4d..57363d8 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -26,22 +26,34 @@ * SUCH DAMAGE. */ +#define _LARGEFILE64_SOURCE + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <limits.h> #include <ctype.h> +#include <getopt.h> #include <sys/time.h> +#include <sys/types.h> + #include <bootimg.h> +#include <sparse/sparse.h> #include <zipfile/zipfile.h> #include "fastboot.h" +#ifndef O_BINARY +#define O_BINARY 0 +#endif + char cur_product[FB_RESPONSE_SZ + 1]; void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline); @@ -58,6 +70,8 @@ static const char *product = 0; static const char *cmdline = 0; static int wipe_data = 0; static unsigned short vendor_id = 0; +static int64_t sparse_limit = -1; +static int64_t target_sparse_limit = -1; static unsigned base_addr = 0x10000000; @@ -88,6 +102,8 @@ char *find_item(const char *item, const char *product) fn = "system.img"; } else if(!strcmp(item,"userdata")) { fn = "userdata.img"; + } else if(!strcmp(item,"cache")) { + fn = "cache.img"; } else if(!strcmp(item,"info")) { fn = "android-info.txt"; } else { @@ -114,7 +130,27 @@ char *find_item(const char *item, const char *product) #ifdef _WIN32 void *load_file(const char *fn, unsigned *_sz); +int64_t file_size(const char *fn); #else +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define off64_t off_t +#endif + +int64_t file_size(const char *fn) +{ + off64_t off; + int fd; + + fd = open(fn, O_RDONLY); + if (fd < 0) return -1; + + off = lseek64(fd, 0, SEEK_END); + close(fd); + + return off; +} + void *load_file(const char *fn, unsigned *_sz) { char *data; @@ -159,6 +195,7 @@ int match_fastboot(usb_ifc_info *info) (info->dev_vendor != 0x0955) && // Nvidia (info->dev_vendor != 0x413c) && // DELL (info->dev_vendor != 0x2314) && // INQ Mobile + (info->dev_vendor != 0x0b05) && // Asus (info->dev_vendor != 0x0bb4)) // HTC return -1; if(info->ifc_class != 0xff) return -1; @@ -223,6 +260,7 @@ void usage(void) " flashall flash boot + recovery + system\n" " flash <partition> [ <filename> ] write a file to a flash partition\n" " erase <partition> erase a flash partition\n" + " format <partition> format a flash partition \n" " getvar <variable> display a bootloader variable\n" " boot <kernel> [ <ramdisk> ] download and boot kernel\n" " flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it\n" @@ -240,6 +278,8 @@ void usage(void) " -i <vendor id> specify a custom USB vendor id\n" " -b <base_addr> specify a custom kernel base address\n" " -n <page size> specify the nand page size. default: 2048\n" + " -S <size>[K|M|G] automatically sparse files greater than\n" + " size. 0 to disable\n" ); } @@ -425,6 +465,110 @@ void queue_info_dump(void) fb_queue_notice("--------------------------------------------"); } + +struct sparse_file **load_sparse_files(const char *fname, int max_size) +{ + int fd; + struct sparse_file *s; + int files; + struct sparse_file **out_s; + + fd = open(fname, O_RDONLY | O_BINARY); + if (fd < 0) { + die("cannot open '%s'\n", fname); + } + + s = sparse_file_import_auto(fd, false); + if (!s) { + die("cannot sparse read file '%s'\n", fname); + } + + files = sparse_file_resparse(s, max_size, NULL, 0); + if (files < 0) { + die("Failed to resparse '%s'\n", fname); + } + + out_s = calloc(sizeof(struct sparse_file *), files + 1); + if (!out_s) { + die("Failed to allocate sparse file array\n"); + } + + files = sparse_file_resparse(s, max_size, out_s, files); + if (files < 0) { + die("Failed to resparse '%s'\n", fname); + } + + return out_s; +} + +static int64_t get_target_sparse_limit(struct usb_handle *usb) +{ + int64_t limit = 0; + char response[FB_RESPONSE_SZ + 1]; + int status = fb_getvar(usb, response, "max-download-size"); + + if (!status) { + limit = strtoul(response, NULL, 0); + if (limit > 0) { + fprintf(stderr, "target reported max download size of %lld bytes\n", + limit); + } + } + + return limit; +} + +static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size) +{ + int64_t limit; + + if (sparse_limit == 0) { + return 0; + } else if (sparse_limit > 0) { + limit = sparse_limit; + } else { + if (target_sparse_limit == -1) { + target_sparse_limit = get_target_sparse_limit(usb); + } + if (target_sparse_limit > 0) { + limit = target_sparse_limit; + } else { + return 0; + } + } + + if (size > limit) { + return limit; + } + + return 0; +} + +void do_flash(usb_handle *usb, const char *pname, const char *fname) +{ + int64_t sz64; + void *data; + int64_t limit; + + sz64 = file_size(fname); + limit = get_sparse_limit(usb, sz64); + if (limit) { + struct sparse_file **s = load_sparse_files(fname, limit); + if (s == NULL) { + die("cannot sparse load '%s'\n", fname); + } + while (*s) { + sz64 = sparse_file_len(*s, true, false); + fb_queue_flash_sparse(pname, *s++, sz64); + } + } else { + unsigned int sz; + data = load_file(fname, &sz); + if (data == 0) die("cannot load '%s'\n", fname); + fb_queue_flash(pname, data, sz); + } +} + void do_update_signature(zipfile_t zip, char *fn) { void *data; @@ -562,6 +706,47 @@ int do_oem_command(int argc, char **argv) return 0; } +static int64_t parse_num(const char *arg) +{ + char *endptr; + unsigned long long num; + + num = strtoull(arg, &endptr, 0); + if (endptr == arg) { + return -1; + } + + if (*endptr == 'k' || *endptr == 'K') { + if (num >= (-1ULL) / 1024) { + return -1; + } + num *= 1024LL; + endptr++; + } else if (*endptr == 'm' || *endptr == 'M') { + if (num >= (-1ULL) / (1024 * 1024)) { + return -1; + } + num *= 1024LL * 1024LL; + endptr++; + } else if (*endptr == 'g' || *endptr == 'G') { + if (num >= (-1ULL) / (1024 * 1024 * 1024)) { + return -1; + } + num *= 1024LL * 1024LL * 1024LL; + endptr++; + } + + if (*endptr != '\0') { + return -1; + } + + if (num > INT64_MAX) { + return -1; + } + + return num; +} + int main(int argc, char **argv) { int wants_wipe = 0; @@ -571,62 +756,83 @@ int main(int argc, char **argv) unsigned sz; unsigned page_size = 2048; int status; + int c; + int r; + + const struct option longopts = { 0, 0, 0, 0 }; + + serial = getenv("ANDROID_SERIAL"); + + while (1) { + c = getopt_long(argc, argv, "wb:n:s:S:p:c:i:m:h", &longopts, NULL); + if (c < 0) { + break; + } + + switch (c) { + case 'w': + wants_wipe = 1; + break; + case 'b': + base_addr = strtoul(optarg, 0, 16); + break; + case 'n': + page_size = (unsigned)strtoul(optarg, NULL, 0); + if (!page_size) die("invalid page size"); + break; + case 's': + serial = optarg; + break; + case 'S': + sparse_limit = parse_num(optarg); + if (sparse_limit < 0) { + die("invalid sparse limit"); + } + break; + case 'p': + product = optarg; + break; + case 'c': + cmdline = optarg; + break; + case 'i': { + char *endptr = NULL; + unsigned long val; + + val = strtoul(optarg, &endptr, 0); + if (!endptr || *endptr != '\0' || (val & ~0xffff)) + die("invalid vendor id '%s'", optarg); + vendor_id = (unsigned short)val; + break; + } + case 'h': + usage(); + return 1; + case '?': + return 1; + default: + abort(); + } + } + + argc -= optind; + argv += optind; - skip(1); - if (argc == 0) { + if (argc == 0 && !wants_wipe) { usage(); return 1; } - if (!strcmp(*argv, "devices")) { + if (argc > 0 && !strcmp(*argv, "devices")) { + skip(1); list_devices(); return 0; } - if (!strcmp(*argv, "help")) { - usage(); - return 0; - } - - - serial = getenv("ANDROID_SERIAL"); + usb = open_device(); while (argc > 0) { - if(!strcmp(*argv, "-w")) { - wants_wipe = 1; - skip(1); - } else if(!strcmp(*argv, "-b")) { - require(2); - base_addr = strtoul(argv[1], 0, 16); - skip(2); - } else if(!strcmp(*argv, "-n")) { - require(2); - page_size = (unsigned)strtoul(argv[1], NULL, 0); - if (!page_size) die("invalid page size"); - skip(2); - } else if(!strcmp(*argv, "-s")) { - require(2); - serial = argv[1]; - skip(2); - } else if(!strcmp(*argv, "-p")) { - require(2); - product = argv[1]; - skip(2); - } else if(!strcmp(*argv, "-c")) { - require(2); - cmdline = argv[1]; - skip(2); - } else if(!strcmp(*argv, "-i")) { - char *endptr = NULL; - unsigned long val; - - require(2); - val = strtoul(argv[1], &endptr, 0); - if (!endptr || *endptr != '\0' || (val & ~0xffff)) - die("invalid vendor id '%s'", argv[1]); - vendor_id = (unsigned short)val; - skip(2); - } else if(!strcmp(*argv, "getvar")) { + if(!strcmp(*argv, "getvar")) { require(2); fb_queue_display(argv[1], argv[1]); skip(2); @@ -634,6 +840,10 @@ int main(int argc, char **argv) require(2); fb_queue_erase(argv[1]); skip(2); + } else if(!strcmp(*argv, "format")) { + require(2); + fb_queue_format(argv[1], 0); + skip(2); } else if(!strcmp(*argv, "signature")) { require(2); data = load_file(argv[1], &sz); @@ -679,9 +889,7 @@ int main(int argc, char **argv) skip(2); } if (fname == 0) die("cannot determine image filename for '%s'", pname); - data = load_file(fname, &sz); - if (data == 0) die("cannot load '%s'\n", fname); - fb_queue_flash(pname, data, sz); + do_flash(usb, pname, fname); } else if(!strcmp(*argv, "flash:raw")) { char *pname = argv[1]; char *kname = argv[2]; @@ -711,6 +919,9 @@ int main(int argc, char **argv) wants_reboot = 1; } else if(!strcmp(*argv, "oem")) { argc = do_oem_command(argc, argv); + } else if (!strcmp(*argv, "help")) { + usage(); + return 0; } else { usage(); return 1; @@ -719,7 +930,9 @@ int main(int argc, char **argv) if (wants_wipe) { fb_queue_erase("userdata"); + fb_queue_format("userdata", 1); fb_queue_erase("cache"); + fb_queue_format("cache", 1); } if (wants_reboot) { fb_queue_reboot(); @@ -727,8 +940,6 @@ int main(int argc, char **argv) fb_queue_command("reboot-bootloader", "rebooting into bootloader"); } - usb = open_device(); - status = fb_execute_queue(usb); return (status) ? 1 : 0; } diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 9e043fe..9177932 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -31,18 +31,24 @@ #include "usb.h" +struct sparse_file; + /* protocol.c - fastboot protocol */ int fb_command(usb_handle *usb, const char *cmd); int fb_command_response(usb_handle *usb, const char *cmd, char *response); int fb_download_data(usb_handle *usb, const void *data, unsigned size); +int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s); char *fb_get_error(void); #define FB_COMMAND_SZ 64 #define FB_RESPONSE_SZ 64 /* engine.c - high level command queue engine */ +int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...); void fb_queue_flash(const char *ptn, void *data, unsigned sz);; +void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz); void fb_queue_erase(const char *ptn); +void fb_queue_format(const char *ptn, int skip_if_not_supported); void fb_queue_require(const char *prod, const char *var, int invert, unsigned nvalues, const char **value); void fb_queue_display(const char *var, const char *prettyname); diff --git a/fastboot/protocol.c b/fastboot/protocol.c index 3948363..a0e0fd4 100644 --- a/fastboot/protocol.c +++ b/fastboot/protocol.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,18 +19,25 @@ * 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 + * 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. */ +#define min(a, b) \ + ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) +#define round_down(a, b) \ + ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); }) + #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> +#include <sparse/sparse.h> + #include "fastboot.h" static char ERROR[128]; @@ -40,8 +47,7 @@ char *fb_get_error(void) return ERROR; } -static int check_response(usb_handle *usb, unsigned size, - unsigned data_okay, char *response) +static int check_response(usb_handle *usb, unsigned int size, char *response) { unsigned char status[65]; int r; @@ -82,7 +88,7 @@ static int check_response(usb_handle *usb, unsigned size, return -1; } - if(!memcmp(status, "DATA", 4) && data_okay){ + if(!memcmp(status, "DATA", 4) && size > 0){ unsigned dsize = strtoul((char*) status + 4, 0, 16); if(dsize > size) { strcpy(ERROR, "data size too large"); @@ -100,13 +106,12 @@ static int check_response(usb_handle *usb, unsigned size, return -1; } -static int _command_send(usb_handle *usb, const char *cmd, - const void *data, unsigned size, - char *response) +static int _command_start(usb_handle *usb, const char *cmd, unsigned size, + char *response) { int cmdsize = strlen(cmd); int r; - + if(response) { response[0] = 0; } @@ -122,56 +127,91 @@ static int _command_send(usb_handle *usb, const char *cmd, return -1; } - if(data == 0) { - return check_response(usb, size, 0, response); + return check_response(usb, size, response); +} + +static int _command_data(usb_handle *usb, const void *data, unsigned size) +{ + int r; + + r = usb_write(usb, data, size); + if(r < 0) { + sprintf(ERROR, "data transfer failure (%s)", strerror(errno)); + usb_close(usb); + return -1; + } + if(r != ((int) size)) { + sprintf(ERROR, "data transfer failure (short transfer)"); + usb_close(usb); + return -1; } - r = check_response(usb, size, 1, 0); + return r; +} + +static int _command_end(usb_handle *usb) +{ + int r; + r = check_response(usb, 0, 0); if(r < 0) { return -1; } - size = r; + return 0; +} - if(size) { - r = usb_write(usb, data, size); - if(r < 0) { - sprintf(ERROR, "data transfer failure (%s)", strerror(errno)); - usb_close(usb); - return -1; - } - if(r != ((int) size)) { - sprintf(ERROR, "data transfer failure (short transfer)"); - usb_close(usb); - return -1; - } +static int _command_send(usb_handle *usb, const char *cmd, + const void *data, unsigned size, + char *response) +{ + int r; + if (size == 0) { + return -1; + } + + r = _command_start(usb, cmd, size, response); + if (r < 0) { + return -1; + } + + r = _command_data(usb, data, size); + if (r < 0) { + return -1; } - - r = check_response(usb, 0, 0, 0); + + r = _command_end(usb); if(r < 0) { return -1; - } else { - return size; } + + return size; +} + +static int _command_send_no_data(usb_handle *usb, const char *cmd, + char *response) +{ + int r; + + return _command_start(usb, cmd, 0, response); } int fb_command(usb_handle *usb, const char *cmd) { - return _command_send(usb, cmd, 0, 0, 0); + return _command_send_no_data(usb, cmd, 0); } int fb_command_response(usb_handle *usb, const char *cmd, char *response) { - return _command_send(usb, cmd, 0, 0, response); + return _command_send_no_data(usb, cmd, response); } int fb_download_data(usb_handle *usb, const void *data, unsigned size) { char cmd[64]; int r; - + sprintf(cmd, "download:%08x", size); r = _command_send(usb, cmd, data, size, 0); - + if(r < 0) { return -1; } else { @@ -179,3 +219,96 @@ int fb_download_data(usb_handle *usb, const void *data, unsigned size) } } +#define USB_BUF_SIZE 512 +static char usb_buf[USB_BUF_SIZE]; +static int usb_buf_len; + +static int fb_download_data_sparse_write(void *priv, const void *data, int len) +{ + int r; + usb_handle *usb = priv; + int to_write; + const char *ptr = data; + + if (usb_buf_len) { + to_write = min(USB_BUF_SIZE - usb_buf_len, len); + + memcpy(usb_buf + usb_buf_len, ptr, to_write); + usb_buf_len += to_write; + ptr += to_write; + len -= to_write; + } + + if (usb_buf_len == USB_BUF_SIZE) { + r = _command_data(usb, usb_buf, USB_BUF_SIZE); + if (r != USB_BUF_SIZE) { + return -1; + } + usb_buf_len = 0; + } + + if (len > USB_BUF_SIZE) { + if (usb_buf_len > 0) { + sprintf(ERROR, "internal error: usb_buf not empty\n"); + return -1; + } + to_write = round_down(len, USB_BUF_SIZE); + r = _command_data(usb, ptr, to_write); + if (r != to_write) { + return -1; + } + ptr += to_write; + len -= to_write; + } + + if (len > 0) { + if (len > USB_BUF_SIZE) { + sprintf(ERROR, "internal error: too much left for usb_buf\n"); + return -1; + } + memcpy(usb_buf, ptr, len); + usb_buf_len = len; + } + + return 0; +} + +static int fb_download_data_sparse_flush(usb_handle *usb) +{ + int r; + + if (usb_buf_len > 0) { + r = _command_data(usb, usb_buf, usb_buf_len); + if (r != usb_buf_len) { + return -1; + } + usb_buf_len = 0; + } + + return 0; +} + +int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s) +{ + char cmd[64]; + int r; + int size = sparse_file_len(s, true, false); + if (size <= 0) { + return -1; + } + + sprintf(cmd, "download:%08x", size); + r = _command_start(usb, cmd, size, 0); + if (r < 0) { + return -1; + } + + r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, usb); + if (r < 0) { + return -1; + } + + fb_download_data_sparse_flush(usb); + + return _command_end(usb); +} diff --git a/fastboot/usb.h b/fastboot/usb.h index cc157d5..df9efde 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -42,19 +42,19 @@ struct usb_ifc_info unsigned char dev_class; unsigned char dev_subclass; unsigned char dev_protocol; - + unsigned char ifc_class; unsigned char ifc_subclass; unsigned char ifc_protocol; unsigned char has_bulk_in; unsigned char has_bulk_out; - + unsigned char writable; char serial_number[256]; }; - + typedef int (*ifc_match_func)(usb_ifc_info *ifc); usb_handle *usb_open(ifc_match_func callback); diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index cbc64e4..83c6de9 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -66,7 +66,7 @@ */ #define MAX_USBFS_BULK_SIZE (16 * 1024) -struct usb_handle +struct usb_handle { char fname[64]; int desc; @@ -85,12 +85,12 @@ static inline int badname(const char *name) static int check(void *_desc, int len, unsigned type, int size) { unsigned char *desc = _desc; - + if(len < size) return -1; if(desc[0] < size) return -1; if(desc[0] > len) return -1; if(desc[1] != type) return -1; - + return 0; } @@ -103,35 +103,37 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, struct usb_interface_descriptor *ifc; struct usb_endpoint_descriptor *ept; struct usb_ifc_info info; - + int in, out; unsigned i; unsigned e; - + if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE)) return -1; dev = (void*) ptr; len -= dev->bLength; ptr += dev->bLength; - + if(check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE)) return -1; cfg = (void*) ptr; len -= cfg->bLength; ptr += cfg->bLength; - + info.dev_vendor = dev->idVendor; info.dev_product = dev->idProduct; info.dev_class = dev->bDeviceClass; info.dev_subclass = dev->bDeviceSubClass; info.dev_protocol = dev->bDeviceProtocol; info.writable = writable; - + // read device serial number (if there is one) info.serial_number[0] = 0; if (dev->iSerialNumber) { struct usbdevfs_ctrltransfer ctrl; - __u16 buffer[128]; + // Keep it short enough because some bootloaders are borked if the URB len is > 255 + // 128 is too big by 1. + __u16 buffer[127]; int result; memset(buffer, 0, sizeof(buffer)); @@ -162,23 +164,23 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, ifc = (void*) ptr; len -= ifc->bLength; ptr += ifc->bLength; - + in = -1; out = -1; info.ifc_class = ifc->bInterfaceClass; info.ifc_subclass = ifc->bInterfaceSubClass; info.ifc_protocol = ifc->bInterfaceProtocol; - + for(e = 0; e < ifc->bNumEndpoints; e++) { if(check(ptr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE)) return -1; ept = (void*) ptr; len -= ept->bLength; ptr += ept->bLength; - + if((ept->bmAttributes & 0x03) != 0x02) continue; - + if(ept->bEndpointAddress & 0x80) { in = ept->bEndpointAddress; } else { @@ -188,7 +190,7 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, info.has_bulk_in = (in != -1); info.has_bulk_out = (out != -1); - + if(callback(&info) == 0) { *ept_in_id = in; *ept_out_id = out; @@ -206,25 +208,25 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) char busname[64], devname[64]; char desc[1024]; int n, in, out, ifc; - + DIR *busdir, *devdir; struct dirent *de; int fd; int writable; - + busdir = opendir(base); if(busdir == 0) return 0; while((de = readdir(busdir)) && (usb == 0)) { if(badname(de->d_name)) continue; - + sprintf(busname, "%s/%s", base, de->d_name); devdir = opendir(busname); if(devdir == 0) continue; - + // DBG("[ scanning %s ]\n", busname); while((de = readdir(devdir)) && (usb == 0)) { - + if(badname(de->d_name)) continue; sprintf(devname, "%s/%s", busname, de->d_name); @@ -240,7 +242,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) } n = read(fd, desc, sizeof(desc)); - + if(filter_usb_device(fd, desc, n, writable, callback, &in, &out, &ifc) == 0) { usb = calloc(1, sizeof(usb_handle)); @@ -277,13 +279,13 @@ int usb_write(usb_handle *h, const void *_data, int len) if(h->ep_out == 0) { return -1; } - + if(len == 0) { bulk.ep = h->ep_out; bulk.len = 0; bulk.data = data; bulk.timeout = 0; - + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); if(n != 0) { fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n", @@ -292,16 +294,16 @@ int usb_write(usb_handle *h, const void *_data, int len) } return 0; } - + while(len > 0) { int xfer; xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; - + bulk.ep = h->ep_out; bulk.len = xfer; bulk.data = data; bulk.timeout = 0; - + n = ioctl(h->desc, USBDEVFS_BULK, &bulk); if(n != xfer) { DBG("ERROR: n = %d, errno = %d (%s)\n", @@ -327,10 +329,10 @@ int usb_read(usb_handle *h, void *_data, int len) if(h->ep_in == 0) { return -1; } - + while(len > 0) { int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; - + bulk.ep = h->ep_in; bulk.len = xfer; bulk.data = data; @@ -353,12 +355,12 @@ int usb_read(usb_handle *h, void *_data, int len) count += n; len -= n; data += n; - + if(n < xfer) { break; } } - + return count; } @@ -377,7 +379,7 @@ void usb_kick(usb_handle *h) int usb_close(usb_handle *h) { int fd; - + fd = h->desc; h->desc = -1; if(fd >= 0) { diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index 6df5d2c..cbce9bd 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -55,7 +55,7 @@ struct usb_handle int success; ifc_match_func callback; usb_ifc_info info; - + UInt8 bulkIn; UInt8 bulkOut; IOUSBInterfaceInterface190 **interface; @@ -132,7 +132,7 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) { // continue so we can try the next interface continue; } - + /* * Now open the interface. This will cause the pipes * associated with the endpoints in the interface descriptor @@ -149,7 +149,7 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) { * use the interface. Maybe something needs to be done about * this situation. */ - + kr = (*interface)->USBInterfaceOpen(interface); if (kr != 0) { @@ -158,7 +158,7 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) { // continue so we can try the next interface continue; } - + // Get the number of endpoints associated with this interface. kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints); @@ -242,10 +242,10 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) { ERR("could not clear output pipe; result %x, ignoring....\n", kr); } } - + return 0; } - + next_interface: (*interface)->USBInterfaceClose(interface); (*interface)->Release(interface); @@ -284,7 +284,7 @@ static int try_device(io_service_t device, usb_handle *handle) { goto error; } - /* + /* * We don't need the intermediate interface after the device interface * is created. */ @@ -339,7 +339,7 @@ static int try_device(io_service_t device, usb_handle *handle) { if (kr == kIOReturnSuccess && req.wLenDone > 0) { int i, count; - + // skip first word, and copy the rest to the serial string, changing shorts to bytes. count = (req.wLenDone - 1) / 2; for (i = 0; i < count; i++) @@ -364,8 +364,8 @@ static int try_device(io_service_t device, usb_handle *handle) { if (dev != NULL) { (*dev)->Release(dev); } - - return -1; + + return -1; } @@ -399,7 +399,7 @@ static int init_usb(ifc_match_func callback, usb_handle **handle) { ERR("Could not create iterator."); return -1; } - + for (;;) { if (! IOIteratorIsValid(iterator)) { /* @@ -409,7 +409,7 @@ static int init_usb(ifc_match_func callback, usb_handle **handle) { IOIteratorReset(iterator); continue; } - + io_service_t device = IOIteratorNext(iterator); if (device == 0) { diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 1050293..99027cc 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -51,13 +51,13 @@ struct usb_handle { /// Handle to USB interface ADBAPIHANDLE adb_interface; - + /// Handle to USB read pipe (endpoint) ADBAPIHANDLE adb_read_pipe; - + /// Handle to USB write pipe (endpoint) ADBAPIHANDLE adb_write_pipe; - + /// Interface name char* interface_name; }; @@ -303,7 +303,7 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) { info.ifc_subclass = interf_desc.bInterfaceSubClass; info.ifc_protocol = interf_desc.bInterfaceProtocol; info.writable = 1; - + // read serial number (if there is one) unsigned long serial_number_len = sizeof(info.serial_number); if (!AdbGetSerialNumber(handle->adb_interface, info.serial_number, diff --git a/fastboot/usbtest.c b/fastboot/usbtest.c index e34d7e6..b8fb9e2 100644 --- a/fastboot/usbtest.c +++ b/fastboot/usbtest.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -38,11 +38,11 @@ static unsigned arg_size = 4096; static unsigned arg_count = 4096; -long long NOW(void) +long long NOW(void) { struct timeval tv; gettimeofday(&tv, 0); - + return (((long long) tv.tv_sec) * ((long long) 1000000)) + (((long long) tv.tv_usec)); } @@ -110,7 +110,7 @@ int test_zero(usb_handle *usb) int i; unsigned char buf[4096]; long long t0, t1; - + t0 = NOW(); for(i = 0; i < arg_count; i++) { if(usb_read(usb, buf, arg_size) != arg_size) { @@ -123,7 +123,7 @@ int test_zero(usb_handle *usb) return 0; } -struct +struct { const char *cmd; ifc_match_func match; @@ -179,12 +179,12 @@ int main(int argc, char **argv) { usb_handle *usb; int i; - + if(argc < 2) return usage(); if(argc > 2) { - if(process_args(argc - 2, argv + 2)) + if(process_args(argc - 2, argv + 2)) return -1; } diff --git a/fastboot/util_linux.c b/fastboot/util_linux.c index 912e16f..91c3776 100644 --- a/fastboot/util_linux.c +++ b/fastboot/util_linux.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -38,7 +38,7 @@ void get_my_path(char *path) { char proc[64]; char *x; - + sprintf(proc, "/proc/%d/exe", getpid()); int err = readlink(proc, path, PATH_MAX - 1); diff --git a/fastboot/util_osx.c b/fastboot/util_osx.c index b43e316..26b832a 100644 --- a/fastboot/util_osx.c +++ b/fastboot/util_osx.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 diff --git a/fastboot/util_windows.c b/fastboot/util_windows.c index 37077a4..9e029fd 100644 --- a/fastboot/util_windows.c +++ b/fastboot/util_windows.c @@ -9,7 +9,7 @@ * 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 + * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -19,7 +19,7 @@ * 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 + * 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 @@ -36,6 +36,29 @@ #include <windows.h> +int64_t file_size(const char *fn) +{ + HANDLE file; + char *data; + DWORD sz; + + file = CreateFile( fn, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (file == INVALID_HANDLE_VALUE) + return -1; + + sz = GetFileSize( file, NULL ); + CloseHandle( file ); + + return sz; +} + void get_my_path(char exe[PATH_MAX]) { char* r; @@ -52,7 +75,7 @@ void *load_file(const char *fn, unsigned *_sz) { HANDLE file; char *data; - DWORD file_size; + DWORD sz; file = CreateFile( fn, GENERIC_READ, @@ -65,29 +88,29 @@ void *load_file(const char *fn, unsigned *_sz) if (file == INVALID_HANDLE_VALUE) return NULL; - file_size = GetFileSize( file, NULL ); + sz = GetFileSize( file, NULL ); data = NULL; - if (file_size > 0) { - data = (char*) malloc( file_size ); + if (sz > 0) { + data = (char*) malloc( sz ); if (data == NULL) { - fprintf(stderr, "load_file: could not allocate %ld bytes\n", file_size ); - file_size = 0; + fprintf(stderr, "load_file: could not allocate %ld bytes\n", sz ); + sz = 0; } else { DWORD out_bytes; - if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) || - out_bytes != file_size ) + if ( !ReadFile( file, data, sz, &out_bytes, NULL ) || + out_bytes != sz ) { - fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", file_size, fn); + fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", sz, fn); free(data); data = NULL; - file_size = 0; + sz = 0; } } } CloseHandle( file ); - *_sz = (unsigned) file_size; + *_sz = (unsigned) sz; return data; } diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk new file mode 100644 index 0000000..7c66f6a --- /dev/null +++ b/fs_mgr/Android.mk @@ -0,0 +1,33 @@ +# Copyright 2011 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= fs_mgr.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include + +LOCAL_MODULE:= libfs_mgr +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +include $(BUILD_STATIC_LIBRARY) + + + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= fs_mgr_main.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include + +LOCAL_MODULE:= fs_mgr + +LOCAL_MODULE_TAGS := optional +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) + +LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils libc + +include $(BUILD_EXECUTABLE) + diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c new file mode 100644 index 0000000..0361ab8 --- /dev/null +++ b/fs_mgr/fs_mgr.c @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* TO DO: + * 1. Re-direct fsck output to the kernel log? + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <libgen.h> +#include <time.h> + +#include <private/android_filesystem_config.h> +#include <cutils/partition_utils.h> +#include <cutils/properties.h> + +#include "fs_mgr_priv.h" + +#define KEY_LOC_PROP "ro.crypto.keyfile.userdata" +#define KEY_IN_FOOTER "footer" + +#define E2FSCK_BIN "/system/bin/e2fsck" + +struct flag_list { + const char *name; + unsigned flag; +}; + +static struct flag_list mount_flags[] = { + { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "defaults", 0 }, + { 0, 0 }, +}; + +static struct flag_list fs_mgr_flags[] = { + { "wait", MF_WAIT }, + { "check", MF_CHECK }, + { "encryptable=",MF_CRYPT }, + { "defaults", 0 }, + { 0, 0 }, +}; + +/* + * 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 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; +} + +static int parse_flags(char *flags, struct flag_list *fl, char **key_loc, + char *fs_options, int fs_options_len) +{ + int f = 0; + int i; + char *p; + char *savep; + + /* initialize key_loc to null, if we find an MF_CRYPT flag, + * then we'll set key_loc to the proper value */ + if (key_loc) { + *key_loc = NULL; + } + /* initialize fs_options to the null string */ + if (fs_options && (fs_options_len > 0)) { + fs_options[0] = '\0'; + } + + p = strtok_r(flags, ",", &savep); + while (p) { + /* Look for the flag "p" in the flag list "fl" + * If not found, the loop exits with fl[i].name being null. + */ + for (i = 0; fl[i].name; i++) { + if (!strncmp(p, fl[i].name, strlen(fl[i].name))) { + f |= fl[i].flag; + if ((fl[i].flag == MF_CRYPT) && key_loc) { + /* The encryptable flag is followed by an = and the + * location of the keys. Get it and return it. + */ + *key_loc = strdup(strchr(p, '=') + 1); + } + break; + } + } + + if (!fl[i].name) { + if (fs_options) { + /* It's not a known flag, so it must be a filesystem specific + * option. Add it to fs_options if it was passed in. + */ + strlcat(fs_options, p, fs_options_len); + strlcat(fs_options, ",", fs_options_len); + } else { + /* fs_options was not passed in, so if the flag is unknown + * it's an error. + */ + ERROR("Warning: unknown flag %s\n", p); + } + } + p = strtok_r(NULL, ",", &savep); + } + +out: + if (fs_options && fs_options[0]) { + /* remove the last trailing comma from the list of options */ + fs_options[strlen(fs_options) - 1] = '\0'; + } + + return f; +} + +/* Read a line of text till the next newline character. + * If no newline is found before the buffer is full, continue reading till a new line is seen, + * then return an empty buffer. This effectively ignores lines that are too long. + * On EOF, return null. + */ +static char *getline(char *buf, int size, FILE *file) +{ + int cnt = 0; + int eof = 0; + int eol = 0; + int c; + + if (size < 1) { + return NULL; + } + + while (cnt < (size - 1)) { + c = getc(file); + if (c == EOF) { + eof = 1; + break; + } + + *(buf + cnt) = c; + cnt++; + + if (c == '\n') { + eol = 1; + break; + } + } + + /* Null terminate what we've read */ + *(buf + cnt) = '\0'; + + if (eof) { + if (cnt) { + return buf; + } else { + return NULL; + } + } else if (eol) { + return buf; + } else { + /* The line is too long. Read till a newline or EOF. + * If EOF, return null, if newline, return an empty buffer. + */ + while(1) { + c = getc(file); + if (c == EOF) { + return NULL; + } else if (c == '\n') { + *buf = '\0'; + return buf; + } + } + } +} + +static struct fstab_rec *read_fstab(char *fstab_path) +{ + FILE *fstab_file; + int cnt, entries; + int len; + char line[256]; + const char *delim = " \t"; + char *save_ptr, *p; + struct fstab_rec *fstab; + char *key_loc; +#define FS_OPTIONS_LEN 1024 + char tmp_fs_options[FS_OPTIONS_LEN]; + + fstab_file = fopen(fstab_path, "r"); + if (!fstab_file) { + ERROR("Cannot open file %s\n", fstab_path); + return 0; + } + + entries = 0; + while (getline(line, sizeof(line), fstab_file)) { + /* if the last character is a newline, shorten the string by 1 byte */ + len = strlen(line); + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } + /* Skip any leading whitespace */ + p = line; + while (isspace(*p)) { + p++; + } + /* ignore comments or empty lines */ + if (*p == '#' || *p == '\0') + continue; + entries++; + } + + if (!entries) { + ERROR("No entries found in fstab\n"); + return 0; + } + + fstab = calloc(entries + 1, sizeof(struct fstab_rec)); + + fseek(fstab_file, 0, SEEK_SET); + + cnt = 0; + while (getline(line, sizeof(line), fstab_file)) { + /* if the last character is a newline, shorten the string by 1 byte */ + len = strlen(line); + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } + + /* Skip any leading whitespace */ + p = line; + while (isspace(*p)) { + p++; + } + /* ignore comments or empty lines */ + if (*p == '#' || *p == '\0') + continue; + + /* If a non-comment entry is greater than the size we allocated, give an + * error and quit. This can happen in the unlikely case the file changes + * between the two reads. + */ + if (cnt >= entries) { + ERROR("Tried to process more entries than counted\n"); + break; + } + + if (!(p = strtok_r(line, delim, &save_ptr))) { + ERROR("Error parsing mount source\n"); + return 0; + } + fstab[cnt].blk_dev = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing mnt_point\n"); + return 0; + } + fstab[cnt].mnt_point = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing fs_type\n"); + return 0; + } + fstab[cnt].type = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing mount_flags\n"); + return 0; + } + tmp_fs_options[0] = '\0'; + fstab[cnt].flags = parse_flags(p, mount_flags, 0, tmp_fs_options, FS_OPTIONS_LEN); + + /* fs_options are optional */ + if (tmp_fs_options[0]) { + fstab[cnt].fs_options = strdup(tmp_fs_options); + } else { + fstab[cnt].fs_options = NULL; + } + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing fs_mgr_options\n"); + return 0; + } + fstab[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, &key_loc, 0, 0); + fstab[cnt].key_loc = key_loc; + + cnt++; + } + fclose(fstab_file); + + return fstab; +} + +static void free_fstab(struct fstab_rec *fstab) +{ + int i = 0; + + while (fstab[i].blk_dev) { + /* Free the pointers return by strdup(3) */ + free(fstab[i].blk_dev); + free(fstab[i].mnt_point); + free(fstab[i].type); + free(fstab[i].fs_options); + free(fstab[i].key_loc); + + i++; + } + + /* Free the actual fstab array created by calloc(3) */ + free(fstab); +} + +static void check_fs(char *blk_dev, char *type) +{ + pid_t pid; + int status; + + /* Check for the types of filesystems we know how to check */ + if (!strcmp(type, "ext2") || !strcmp(type, "ext3") || !strcmp(type, "ext4")) { + INFO("Running %s on %s\n", E2FSCK_BIN, blk_dev); + pid = fork(); + if (pid > 0) { + /* Parent, wait for the child to return */ + waitpid(pid, &status, 0); + } else if (pid == 0) { + /* child, run checker */ + execlp(E2FSCK_BIN, E2FSCK_BIN, "-y", blk_dev, (char *)NULL); + + /* Only gets here on error */ + ERROR("Cannot run fs_mgr binary %s\n", E2FSCK_BIN); + } else { + /* No need to check for error in fork, we can't really handle it now */ + ERROR("Fork failed trying to run %s\n", E2FSCK_BIN); + } + } + + return; +} + +static void remove_trailing_slashes(char *n) +{ + int len; + + len = strlen(n) - 1; + while ((*(n + len) == '/') && len) { + *(n + len) = '\0'; + len--; + } +} + +static int fs_match(char *in1, char *in2) +{ + char *n1; + char *n2; + int ret; + + n1 = strdup(in1); + n2 = strdup(in2); + + remove_trailing_slashes(n1); + remove_trailing_slashes(n2); + + ret = !strcmp(n1, n2); + + free(n1); + free(n2); + + return ret; +} + +int fs_mgr_mount_all(char *fstab_file) +{ + int i = 0; + int encrypted = 0; + int ret = -1; + int mret; + struct fstab_rec *fstab = 0; + + if (!(fstab = read_fstab(fstab_file))) { + return ret; + } + + for (i = 0; fstab[i].blk_dev; i++) { + if (fstab[i].fs_mgr_flags & MF_WAIT) { + wait_for_file(fstab[i].blk_dev, WAIT_TIMEOUT); + } + + if (fstab[i].fs_mgr_flags & MF_CHECK) { + check_fs(fstab[i].blk_dev, fstab[i].type); + } + + mret = mount(fstab[i].blk_dev, fstab[i].mnt_point, fstab[i].type, + fstab[i].flags, fstab[i].fs_options); + if (!mret) { + /* Success! Go get the next one */ + continue; + } + + /* mount(2) returned an error, check if it's encrypted and deal with it */ + if ((fstab[i].fs_mgr_flags & MF_CRYPT) && !partition_wiped(fstab[i].blk_dev)) { + /* Need to mount a tmpfs at this mountpoint for now, and set + * properties that vold will query later for decrypting + */ + if (mount("tmpfs", fstab[i].mnt_point, "tmpfs", + MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) { + ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s\n", + fstab[i].mnt_point); + goto out; + } + encrypted = 1; + } else { + ERROR("Cannot mount filesystem on %s at %s\n", + fstab[i].blk_dev, fstab[i].mnt_point); + goto out; + } + } + + if (encrypted) { + ret = 1; + } else { + ret = 0; + } + +out: + free_fstab(fstab); + return ret; +} + +/* If tmp_mnt_point is non-null, mount the filesystem there. This is for the + * tmp mount we do to check the user password + */ +int fs_mgr_do_mount(char *fstab_file, char *n_name, char *n_blk_dev, char *tmp_mnt_point) +{ + int i = 0; + int ret = -1; + struct fstab_rec *fstab = 0; + char *m; + + if (!(fstab = read_fstab(fstab_file))) { + return ret; + } + + for (i = 0; fstab[i].blk_dev; i++) { + if (!fs_match(fstab[i].mnt_point, n_name)) { + continue; + } + + /* We found our match */ + /* First check the filesystem if requested */ + if (fstab[i].fs_mgr_flags & MF_WAIT) { + wait_for_file(fstab[i].blk_dev, WAIT_TIMEOUT); + } + + if (fstab[i].fs_mgr_flags & MF_CHECK) { + check_fs(fstab[i].blk_dev, fstab[i].type); + } + + /* Now mount it where requested */ + if (tmp_mnt_point) { + m = tmp_mnt_point; + } else { + m = fstab[i].mnt_point; + } + if (mount(n_blk_dev, m, fstab[i].type, + fstab[i].flags, fstab[i].fs_options)) { + ERROR("Cannot mount filesystem on %s at %s\n", + n_blk_dev, m); + goto out; + } else { + ret = 0; + goto out; + } + } + + /* We didn't find a match, say so and return an error */ + ERROR("Cannot find mount point %s in fstab\n", fstab[i].mnt_point); + +out: + free_fstab(fstab); + return ret; +} + +/* + * mount a tmpfs filesystem at the given point. + * return 0 on success, non-zero on failure. + */ +int fs_mgr_do_tmpfs_mount(char *n_name) +{ + int ret; + + ret = mount("tmpfs", n_name, "tmpfs", + MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS); + if (ret < 0) { + ERROR("Cannot mount tmpfs filesystem at %s\n", n_name); + return -1; + } + + /* Success */ + return 0; +} + +int fs_mgr_unmount_all(char *fstab_file) +{ + int i = 0; + int ret = 0; + struct fstab_rec *fstab = 0; + + if (!(fstab = read_fstab(fstab_file))) { + return -1; + } + + while (fstab[i].blk_dev) { + if (umount(fstab[i].mnt_point)) { + ERROR("Cannot unmount filesystem at %s\n", fstab[i].mnt_point); + ret = -1; + } + i++; + } + + free_fstab(fstab); + return ret; +} +/* + * key_loc must be at least PROPERTY_VALUE_MAX bytes long + * + * real_blk_dev must be at least PROPERTY_VALUE_MAX bytes long + */ +int fs_mgr_get_crypt_info(char *fstab_file, char *key_loc, char *real_blk_dev, int size) +{ + int i = 0; + struct fstab_rec *fstab = 0; + + if (!(fstab = read_fstab(fstab_file))) { + return -1; + } + /* Initialize return values to null strings */ + if (key_loc) { + *key_loc = '\0'; + } + if (real_blk_dev) { + *real_blk_dev = '\0'; + } + + /* Look for the encryptable partition to find the data */ + for (i = 0; fstab[i].blk_dev; i++) { + if (!(fstab[i].fs_mgr_flags & MF_CRYPT)) { + continue; + } + + /* We found a match */ + if (key_loc) { + strlcpy(key_loc, fstab[i].key_loc, size); + } + if (real_blk_dev) { + strlcpy(real_blk_dev, fstab[i].blk_dev, size); + } + break; + } + + free_fstab(fstab); + return 0; +} + diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c new file mode 100644 index 0000000..81febf1 --- /dev/null +++ b/fs_mgr/fs_mgr_main.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <libgen.h> +#include "fs_mgr_priv.h" + +char *me = ""; + +static void usage(void) +{ + ERROR("%s: usage: %s <-a | -n mnt_point blk_dev | -u> <fstab_file>\n", me, me); + exit(1); +} + +/* Parse the command line. If an error is encountered, print an error message + * and exit the program, do not return to the caller. + * Return the number of argv[] entries consumed. + */ +static void parse_options(int argc, char *argv[], int *a_flag, int *u_flag, int *n_flag, + char **n_name, char **n_blk_dev) +{ + me = basename(strdup(argv[0])); + + if (argc <= 1) { + usage(); + } + + if (!strcmp(argv[1], "-a")) { + if (argc != 3) { + usage(); + } + *a_flag = 1; + } + if (!strcmp(argv[1], "-n")) { + if (argc != 5) { + usage(); + } + *n_flag = 1; + *n_name = argv[2]; + *n_blk_dev = argv[3]; + } + if (!strcmp(argv[1], "-u")) { + if (argc != 3) { + usage(); + } + *u_flag = 1; + } + + /* If no flag is specified, it's an error */ + if (!(*a_flag | *n_flag | *u_flag)) { + usage(); + } + + /* If more than one flag is specified, it's an error */ + if ((*a_flag + *n_flag + *u_flag) > 1) { + usage(); + } + + return; +} + +int main(int argc, char *argv[]) +{ + int a_flag=0; + int u_flag=0; + int n_flag=0; + char *n_name; + char *n_blk_dev; + char *fstab; + + klog_init(); + klog_set_level(6); + + parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev); + + /* The name of the fstab file is last, after the option */ + fstab = argv[argc - 1]; + + if (a_flag) { + return fs_mgr_mount_all(fstab); + } else if (n_flag) { + return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0); + } else if (u_flag) { + return fs_mgr_unmount_all(fstab); + } else { + ERROR("%s: Internal error, unknown option\n", me); + exit(1); + } + + /* Should not get here */ + exit(1); +} + diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h new file mode 100644 index 0000000..175fdab --- /dev/null +++ b/fs_mgr/fs_mgr_priv.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CORE_FS_MGR_PRIV_H +#define __CORE_FS_MGR_PRIV_H + +#include <cutils/klog.h> +#include <fs_mgr.h> + +#define INFO(x...) KLOG_INFO("fs_mgr", x) +#define ERROR(x...) KLOG_ERROR("fs_mgr", x) + +#define CRYPTO_TMPFS_OPTIONS "size=128m,mode=0771,uid=1000,gid=1000" + +struct fstab_rec { + char *blk_dev; + char *mnt_point; + char *type; + unsigned long flags; + char *fs_options; + int fs_mgr_flags; + char *key_loc; +}; + +#define WAIT_TIMEOUT 5 + +/* fstab has the following format: + * + * Any line starting with a # is a comment and ignored + * + * Any blank line is ignored + * + * All other lines must be in this format: + * <source> <mount_point> <fs_type> <mount_flags> <fs_options> <fs_mgr_options> + * + * <mount_flags> is a comma separated list of flags that can be passed to the + * mount command. The list includes noatime, nosuid, nodev, nodiratime, + * ro, rw, remount, defaults. + * + * <fs_options> is a comma separated list of options accepted by the filesystem being + * mounted. It is passed directly to mount without being parsed + * + * <fs_mgr_options> is a comma separated list of flags that control the operation of + * the fs_mgr program. The list includes "wait", which will wait till + * the <source> file exists, and "check", which requests that the fs_mgr + * run an fscheck program on the <source> before mounting the filesystem. + * If check is specifed on a read-only filesystem, it is ignored. + * Also, "encryptable" means that filesystem can be encrypted. + * The "encryptable" flag _MUST_ be followed by a : and a string which + * is the location of the encryption keys. I can either be a path + * to a file or partition which contains the keys, or the word "footer" + * which means the keys are in the last 16 Kbytes of the partition + * containing the filesystem. + * + * When the fs_mgr is requested to mount all filesystems, it will first mount all the + * filesystems that do _NOT_ specify check (including filesystems that are read-only and + * specify check, because check is ignored in that case) and then it will check and mount + * filesystem marked with check. + * + */ + +#define MF_WAIT 0x1 +#define MF_CHECK 0x2 +#define MF_CRYPT 0x4 + +#endif /* __CORE_FS_MGR_PRIV_H */ + diff --git a/nexus/NexusCommand.h b/fs_mgr/include/fs_mgr.h index a7f944a..76abb83 100644 --- a/nexus/NexusCommand.h +++ b/fs_mgr/include/fs_mgr.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,14 @@ * limitations under the License. */ -#ifndef _NEXUS_COMMAND_H -#define _NEXUS_COMMAND_H +#ifndef __CORE_FS_MGR_H +#define __CORE_FS_MGR_H -#include <sysutils/FrameworkCommand.h> +int fs_mgr_mount_all(char *fstab_file); +int fs_mgr_do_mount(char *fstab_file, char *n_name, char *n_blk_dev, char *tmp_mnt_point); +int fs_mgr_do_tmpfs_mount(char *n_name); +int fs_mgr_unmount_all(char *fstab_file); +int fs_mgr_get_crypt_info(char *fstab_file, char *key_loc, char *real_blk_dev, int size); -class NexusCommand : public FrameworkCommand { -public: - NexusCommand(const char *cmd); - virtual ~NexusCommand() {} -}; +#endif /* __CORE_FS_MGR_H */ -#endif diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h index c8ccc7e..48f8d9a 100644 --- a/include/arch/darwin-x86/AndroidConfig.h +++ b/include/arch/darwin-x86/AndroidConfig.h @@ -109,7 +109,7 @@ /* * Define this if we have localtime_r(). */ -#define HAVE_LOCALTIME_R +#define HAVE_LOCALTIME_R 1 /* * Define this if we have gethostbyname_r(). @@ -305,4 +305,14 @@ */ #define HAVE_PRINTF_ZD 1 +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 1 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#define HAVE_GNU_QSORT_R 0 + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/freebsd-x86/AndroidConfig.h b/include/arch/freebsd-x86/AndroidConfig.h index d828bd5..4bc5559 100644 --- a/include/arch/freebsd-x86/AndroidConfig.h +++ b/include/arch/freebsd-x86/AndroidConfig.h @@ -114,7 +114,7 @@ /* * Define this if we have localtime_r(). */ -#define HAVE_LOCALTIME_R +#define HAVE_LOCALTIME_R 1 /* * Define this if we have gethostbyname_r(). @@ -363,4 +363,14 @@ */ #define HAVE_PRINTF_ZD 1 +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 1 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#define HAVE_GNU_QSORT_R 0 + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h index 7eba7d0..233752b 100644 --- a/include/arch/linux-arm/AndroidConfig.h +++ b/include/arch/linux-arm/AndroidConfig.h @@ -122,7 +122,7 @@ /* * Define this if we have localtime_r(). */ -/* #define HAVE_LOCALTIME_R */ +/* #define HAVE_LOCALTIME_R 1 */ /* * Define this if we have gethostbyname_r(). @@ -361,4 +361,14 @@ */ #define HAVE_PRINTF_ZD 1 +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 0 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#define HAVE_GNU_QSORT_R 0 + #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-ppc/AndroidConfig.h b/include/arch/linux-ppc/AndroidConfig.h index 2ec99f0..ae2569b 100644 --- a/include/arch/linux-ppc/AndroidConfig.h +++ b/include/arch/linux-ppc/AndroidConfig.h @@ -109,7 +109,7 @@ /* * Define this if we have localtime_r(). */ -#define HAVE_LOCALTIME_R +#define HAVE_LOCALTIME_R 1 /* * Define this if we have gethostbyname_r(). @@ -323,4 +323,14 @@ */ #define HAVE_PREAD 1 +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 0 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#define HAVE_GNU_QSORT_R 1 + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-sh/AndroidConfig.h b/include/arch/linux-sh/AndroidConfig.h index f51caeb..818b628 100644 --- a/include/arch/linux-sh/AndroidConfig.h +++ b/include/arch/linux-sh/AndroidConfig.h @@ -122,7 +122,7 @@ /* * Define this if we have localtime_r(). */ -/* #define HAVE_LOCALTIME_R */ +/* #define HAVE_LOCALTIME_R 1 */ /* * Define this if we have gethostbyname_r(). @@ -366,4 +366,14 @@ */ #define HAVE_PRINTF_ZD 1 +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 0 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#define HAVE_GNU_QSORT_R 0 + #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h index 24e7797..431a54b 100644 --- a/include/arch/linux-x86/AndroidConfig.h +++ b/include/arch/linux-x86/AndroidConfig.h @@ -109,7 +109,7 @@ /* * Define this if we have localtime_r(). */ -#define HAVE_LOCALTIME_R +#define HAVE_LOCALTIME_R 1 /* * Define this if we have gethostbyname_r(). @@ -333,4 +333,18 @@ */ #define HAVE_PRINTF_ZD 1 +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 0 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +#define HAVE_GNU_QSORT_R 1 +#else +#define HAVE_GNU_QSORT_R 0 +#endif + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h index 05dd220..ab53892 100644 --- a/include/arch/target_linux-x86/AndroidConfig.h +++ b/include/arch/target_linux-x86/AndroidConfig.h @@ -108,7 +108,7 @@ /* * Define this if we have localtime_r(). */ -/* #define HAVE_LOCALTIME_R */ +/* #define HAVE_LOCALTIME_R 1 */ /* * Define this if we have gethostbyname_r(). @@ -350,4 +350,14 @@ */ #define HAVE_PRINTF_ZD 1 +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 0 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#define HAVE_GNU_QSORT_R 0 + #endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/windows/AndroidConfig.h b/include/arch/windows/AndroidConfig.h index ad890b4..0274da5 100644 --- a/include/arch/windows/AndroidConfig.h +++ b/include/arch/windows/AndroidConfig.h @@ -125,7 +125,7 @@ /* * Define this if we have localtime_r(). */ -/* #define HAVE_LOCALTIME_R */ +/* #define HAVE_LOCALTIME_R 1 */ /* * Define this if we have gethostbyname_r(). @@ -338,4 +338,14 @@ */ /* #define HAVE_PRINTF_ZD 1 */ +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a BSD style function prototype. + */ +#define HAVE_BSD_QSORT_R 0 + +/* + * Define to 1 if <stdlib.h> provides qsort_r() with a GNU style function prototype. + */ +#define HAVE_GNU_QSORT_R 0 + #endif /*_ANDROID_CONFIG_H*/ diff --git a/include/corkscrew/backtrace.h b/include/corkscrew/backtrace.h new file mode 100644 index 0000000..556ad04 --- /dev/null +++ b/include/corkscrew/backtrace.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2011 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. + */ + +/* A stack unwinder. */ + +#ifndef _CORKSCREW_BACKTRACE_H +#define _CORKSCREW_BACKTRACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <corkscrew/ptrace.h> +#include <corkscrew/map_info.h> +#include <corkscrew/symbol_table.h> + +/* + * Describes a single frame of a backtrace. + */ +typedef struct { + uintptr_t absolute_pc; /* absolute PC offset */ + uintptr_t stack_top; /* top of stack for this frame */ + size_t stack_size; /* size of this stack frame */ +} backtrace_frame_t; + +/* + * Describes the symbols associated with a backtrace frame. + */ +typedef struct { + uintptr_t relative_pc; /* relative frame PC offset from the start of the library, + or the absolute PC if the library is unknown */ + uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the + library or 0 if the library is unknown */ + char* map_name; /* executable or library name, or NULL if unknown */ + char* symbol_name; /* symbol name, or NULL if unknown */ + char* demangled_name; /* demangled symbol name, or NULL if unknown */ +} backtrace_symbol_t; + +/* + * Unwinds the call stack for the current thread of execution. + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + */ +ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); + +/* + * Unwinds the call stack for a thread within this process. + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + * + * The task is briefly suspended while the backtrace is being collected. + */ +ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth); + +/* + * Unwinds the call stack of a task within a remote process using ptrace(). + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + */ +ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); + +/* + * Gets the symbols for each frame of a backtrace. + * The symbols array must be big enough to hold one symbol record per frame. + * The symbols must later be freed using free_backtrace_symbols. + */ +void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + +/* + * Gets the symbols for each frame of a backtrace from a remote process. + * The symbols array must be big enough to hold one symbol record per frame. + * The symbols must later be freed using free_backtrace_symbols. + */ +void get_backtrace_symbols_ptrace(const ptrace_context_t* context, + const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + +/* + * Frees the storage associated with backtrace symbols. + */ +void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames); + +enum { + // A hint for how big to make the line buffer for format_backtrace_line + MAX_BACKTRACE_LINE_LENGTH = 800, +}; + +/** + * Formats a line from a backtrace as a zero-terminated string into the specified buffer. + */ +void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame, + const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_BACKTRACE_H diff --git a/nexus/OpenVpnController.h b/include/corkscrew/demangle.h index 529aab5..04b0225 100644 --- a/nexus/OpenVpnController.h +++ b/include/corkscrew/demangle.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 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. @@ -14,29 +14,29 @@ * limitations under the License. */ -#ifndef _OPEN_VPN_CONTROLLER_H -#define _OPEN_VPN_CONTROLLER_H +/* C++ symbol name demangling. */ -#include "PropertyManager.h" -#include "VpnController.h" +#ifndef _CORKSCREW_DEMANGLE_H +#define _CORKSCREW_DEMANGLE_H -class ServiceManager; -class IControllerHandler; +#include <sys/types.h> +#include <stdbool.h> -class OpenVpnController : public VpnController { -private: - ServiceManager *mServiceManager; - -public: - OpenVpnController(PropertyManager *propmngr, IControllerHandler *handlers); - virtual ~OpenVpnController(); - - int start(); - int stop(); +#ifdef __cplusplus +extern "C" { +#endif -private: - int enable(); - int disable(); -}; +/* + * Demangles a C++ symbol name. + * If name is NULL or if the name cannot be demangled, returns NULL. + * Otherwise, returns a newly allocated string that contains the demangled name. + * + * The caller must free the returned string using free(). + */ +char* demangle_symbol_name(const char* name); +#ifdef __cplusplus +} #endif + +#endif // _CORKSCREW_DEMANGLE_H diff --git a/include/corkscrew/map_info.h b/include/corkscrew/map_info.h new file mode 100644 index 0000000..c5cd8f8 --- /dev/null +++ b/include/corkscrew/map_info.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2011 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. + */ + +/* Process memory map. */ + +#ifndef _CORKSCREW_MAP_INFO_H +#define _CORKSCREW_MAP_INFO_H + +#include <sys/types.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct map_info { + struct map_info* next; + uintptr_t start; + uintptr_t end; + bool is_readable; + bool is_executable; + void* data; // arbitrary data associated with the map by the user, initially NULL + char name[]; +} map_info_t; + +/* Loads memory map from /proc/<tid>/maps. */ +map_info_t* load_map_info_list(pid_t tid); + +/* Frees memory map. */ +void free_map_info_list(map_info_t* milist); + +/* Finds the memory map that contains the specified address. */ +const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr); + +/* Returns true if the addr is in an readable map. */ +bool is_readable_map(const map_info_t* milist, uintptr_t addr); + +/* Returns true if the addr is in an executable map. */ +bool is_executable_map(const map_info_t* milist, uintptr_t addr); + +/* Acquires a reference to the memory map for this process. + * The result is cached and refreshed automatically. + * Make sure to release the map info when done. */ +map_info_t* acquire_my_map_info_list(); + +/* Releases a reference to the map info for this process that was + * previous acquired using acquire_my_map_info_list(). */ +void release_my_map_info_list(map_info_t* milist); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_MAP_INFO_H diff --git a/include/corkscrew/ptrace.h b/include/corkscrew/ptrace.h new file mode 100644 index 0000000..172e348 --- /dev/null +++ b/include/corkscrew/ptrace.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 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. + */ + +/* Useful ptrace() utility functions. */ + +#ifndef _CORKSCREW_PTRACE_H +#define _CORKSCREW_PTRACE_H + +#include <corkscrew/map_info.h> +#include <corkscrew/symbol_table.h> + +#include <sys/types.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Stores information about a process that is used for several different + * ptrace() based operations. */ +typedef struct { + map_info_t* map_info_list; +} ptrace_context_t; + +/* Describes how to access memory from a process. */ +typedef struct { + pid_t tid; + const map_info_t* map_info_list; +} memory_t; + +#if __i386__ +/* ptrace() register context. */ +typedef struct pt_regs_x86 { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t xds; + uint32_t xes; + uint32_t xfs; + uint32_t xgs; + uint32_t orig_eax; + uint32_t eip; + uint32_t xcs; + uint32_t eflags; + uint32_t esp; + uint32_t xss; +} pt_regs_x86_t; +#endif + +/* + * Initializes a memory structure for accessing memory from this process. + */ +void init_memory(memory_t* memory, const map_info_t* map_info_list); + +/* + * Initializes a memory structure for accessing memory from another process + * using ptrace(). + */ +void init_memory_ptrace(memory_t* memory, pid_t tid); + +/* + * Reads a word of memory safely. + * If the memory is local, ensures that the address is readable before dereferencing it. + * Returns false and a value of 0xffffffff if the word could not be read. + */ +bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value); + +/* + * Reads a word of memory safely using ptrace(). + * Returns false and a value of 0xffffffff if the word could not be read. + */ +bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value); + +/* + * Loads information needed for examining a remote process using ptrace(). + * The caller must already have successfully attached to the process + * using ptrace(). + * + * The context can be used for any threads belonging to that process + * assuming ptrace() is attached to them before performing the actual + * unwinding. The context can continue to be used to decode backtraces + * even after ptrace() has been detached from the process. + */ +ptrace_context_t* load_ptrace_context(pid_t pid); + +/* + * Frees a ptrace context. + */ +void free_ptrace_context(ptrace_context_t* context); + +/* + * Finds a symbol using ptrace. + * Returns the containing map and information about the symbol, or + * NULL if one or the other is not available. + */ +void find_symbol_ptrace(const ptrace_context_t* context, + uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_PTRACE_H diff --git a/include/corkscrew/symbol_table.h b/include/corkscrew/symbol_table.h new file mode 100644 index 0000000..020c8b8 --- /dev/null +++ b/include/corkscrew/symbol_table.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 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 _CORKSCREW_SYMBOL_TABLE_H +#define _CORKSCREW_SYMBOL_TABLE_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uintptr_t start; + uintptr_t end; + char* name; +} symbol_t; + +typedef struct { + symbol_t* symbols; + size_t num_symbols; +} symbol_table_t; + +/* + * Loads a symbol table from a given file. + * Returns NULL on error. + */ +symbol_table_t* load_symbol_table(const char* filename); + +/* + * Frees a symbol table. + */ +void free_symbol_table(symbol_table_t* table); + +/* + * Finds a symbol associated with an address in the symbol table. + * Returns NULL if not found. + */ +const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_SYMBOL_TABLE_H diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h new file mode 100644 index 0000000..5a8e796 --- /dev/null +++ b/include/cutils/debugger.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_DEBUGGER_H +#define __CUTILS_DEBUGGER_H + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEBUGGER_SOCKET_NAME "android:debuggerd" + +typedef enum { + // dump a crash + DEBUGGER_ACTION_CRASH, + // dump a tombstone file + DEBUGGER_ACTION_DUMP_TOMBSTONE, + // dump a backtrace only back to the socket + DEBUGGER_ACTION_DUMP_BACKTRACE, +} debugger_action_t; + +/* message sent over the socket */ +typedef struct { + debugger_action_t action; + pid_t tid; +} debugger_msg_t; + +/* Dumps a process backtrace, registers, and stack to a tombstone file (requires root). + * Stores the tombstone path in the provided buffer. + * Returns 0 on success, -1 on error. + */ +int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen); + +/* Dumps a process backtrace only to the specified file (requires root). + * Returns 0 on success, -1 on error. + */ +int dump_backtrace_to_file(pid_t tid, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_DEBUGGER_H */ diff --git a/include/cutils/list.h b/include/cutils/list.h index eb5a3c8..3881fc9 100644 --- a/include/cutils/list.h +++ b/include/cutils/list.h @@ -19,6 +19,10 @@ #include <stddef.h> +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + struct listnode { struct listnode *next; @@ -48,4 +52,8 @@ void list_remove(struct listnode *item); #define list_head(list) ((list)->next) #define list_tail(list) ((list)->prev) +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + #endif diff --git a/include/cutils/log.h b/include/cutils/log.h index 2997a0c..878952e 100644 --- a/include/cutils/log.h +++ b/include/cutils/log.h @@ -79,10 +79,6 @@ extern "C" { #else #define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #endif -// Temporary measure for code still using old LOG macros. -#ifndef LOGV -#define LOGV ALOGV -#endif #endif #define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) @@ -96,10 +92,6 @@ extern "C" { ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) #endif -// Temporary measure for code still using old LOG macros. -#ifndef LOGV_IF -#define LOGV_IF ALOGV_IF -#endif #endif /* @@ -107,10 +99,6 @@ extern "C" { */ #ifndef ALOGD #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGD -#define LOGD ALOGD -#endif #endif #ifndef ALOGD_IF @@ -118,10 +106,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGD_IF -#define LOGD_IF ALOGD_IF -#endif #endif /* @@ -129,10 +113,6 @@ extern "C" { */ #ifndef ALOGI #define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGI -#define LOGI ALOGI -#endif #endif #ifndef ALOGI_IF @@ -140,10 +120,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGI_IF -#define LOGI_IF ALOGI_IF -#endif #endif /* @@ -151,10 +127,6 @@ extern "C" { */ #ifndef ALOGW #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGW -#define LOGW ALOGW -#endif #endif #ifndef ALOGW_IF @@ -162,10 +134,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGW_IF -#define LOGW_IF ALOGW_IF -#endif #endif /* @@ -173,10 +141,6 @@ extern "C" { */ #ifndef ALOGE #define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGE -#define LOGE ALOGE -#endif #endif #ifndef ALOGE_IF @@ -184,10 +148,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGE_IF -#define LOGE_IF ALOGE_IF -#endif #endif // --------------------------------------------------------------------- @@ -202,10 +162,6 @@ extern "C" { #else #define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) #endif -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGV -#define IF_LOGV IF_ALOGV -#endif #endif /* @@ -214,10 +170,6 @@ extern "C" { */ #ifndef IF_ALOGD #define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGD -#define IF_LOGD IF_ALOGD -#endif #endif /* @@ -226,10 +178,6 @@ extern "C" { */ #ifndef IF_ALOGI #define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGI -#define IF_LOGI IF_ALOGI -#endif #endif /* @@ -238,10 +186,6 @@ extern "C" { */ #ifndef IF_ALOGW #define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGW -#define IF_LOGW IF_ALOGW -#endif #endif /* @@ -250,10 +194,6 @@ extern "C" { */ #ifndef IF_ALOGE #define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGE -#define IF_LOGE IF_ALOGE -#endif #endif @@ -392,10 +332,6 @@ extern "C" { #ifndef ALOG_ASSERT #define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) //#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) -// Temporary measure for code still using old LOG macros. -#ifndef LOG_ASSERT -#define LOG_ASSERT ALOG_ASSERT -#endif #endif // --------------------------------------------------------------------- @@ -411,10 +347,6 @@ extern "C" { #ifndef ALOG #define ALOG(priority, tag, ...) \ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) -// Temporary measure for code still using old LOG macros. -#ifndef LOG -#define LOG ALOG -#endif #endif /* @@ -439,10 +371,6 @@ extern "C" { #ifndef IF_ALOG #define IF_ALOG(priority, tag) \ if (android_testLog(ANDROID_##priority, tag)) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOG -#define IF_LOG IF_ALOG -#endif #endif // --------------------------------------------------------------------- diff --git a/include/cutils/logger.h b/include/cutils/logger.h index b60f7ad..04f3fb0 100644 --- a/include/cutils/logger.h +++ b/include/cutils/logger.h @@ -12,6 +12,11 @@ #include <stdint.h> +/* + * The userspace structure for version 1 of the logger_entry ABI. + * This structure is returned to userspace by the kernel logger + * driver unless an upgrade to a newer ABI version is requested. + */ struct logger_entry { uint16_t len; /* length of the payload */ uint16_t __pad; /* no matter what, we get 2 bytes of padding */ @@ -22,14 +27,41 @@ struct logger_entry { char msg[0]; /* the entry's payload */ }; +/* + * The userspace structure for version 2 of the logger_entry ABI. + * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) + * is called with version==2 + */ +struct logger_entry_v2 { + uint16_t len; /* length of the payload */ + uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + uint32_t euid; /* effective UID of logger */ + char msg[0]; /* the entry's payload */ +}; + #define LOGGER_LOG_MAIN "log/main" #define LOGGER_LOG_RADIO "log/radio" #define LOGGER_LOG_EVENTS "log/events" #define LOGGER_LOG_SYSTEM "log/system" -#define LOGGER_ENTRY_MAX_LEN (4*1024) -#define LOGGER_ENTRY_MAX_PAYLOAD \ - (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) +/* + * The maximum size of the log entry payload that can be + * written to the kernel logger driver. An attempt to write + * more than this amount to /dev/log/* will result in a + * truncated log entry. + */ +#define LOGGER_ENTRY_MAX_PAYLOAD 4076 + +/* + * The maximum size of a log entry which can be read from the + * kernel logger driver. An attempt to read less than this amount + * may result in read() returning EINVAL. + */ +#define LOGGER_ENTRY_MAX_LEN (5*1024) #ifdef HAVE_IOCTL @@ -41,6 +73,8 @@ struct logger_entry { #define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ #define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ #endif // HAVE_IOCTL diff --git a/include/cutils/logprint.h b/include/cutils/logprint.h index 769c8a7..2b1e1c5 100644 --- a/include/cutils/logprint.h +++ b/include/cutils/logprint.h @@ -44,8 +44,8 @@ typedef struct AndroidLogEntry_t { time_t tv_sec; long tv_nsec; android_LogPriority priority; - pid_t pid; - pthread_t tid; + int32_t pid; + int32_t tid; const char * tag; size_t messageLen; const char * message; diff --git a/include/cutils/qsort_r_compat.h b/include/cutils/qsort_r_compat.h new file mode 100644 index 0000000..479a1ab --- /dev/null +++ b/include/cutils/qsort_r_compat.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Provides a portable version of qsort_r, called qsort_r_compat, which is a + * reentrant variant of qsort that passes a user data pointer to its comparator. + * This implementation follows the BSD parameter convention. + */ + +#ifndef _LIBS_CUTILS_QSORT_R_COMPAT_H +#define _LIBS_CUTILS_QSORT_R_COMPAT_H + +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void* )); + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_CUTILS_QSORT_R_COMPAT_H diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h index eaf3993..ba84ce3 100644 --- a/include/cutils/sched_policy.h +++ b/include/cutils/sched_policy.h @@ -21,14 +21,39 @@ extern "C" { #endif +/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */ typedef enum { + SP_DEFAULT = -1, SP_BACKGROUND = 0, SP_FOREGROUND = 1, + SP_SYSTEM = 2, // can't be used with set_sched_policy() + SP_AUDIO_APP = 3, + SP_AUDIO_SYS = 4, + SP_CNT, + SP_MAX = SP_CNT - 1, + SP_SYSTEM_DEFAULT = SP_FOREGROUND, } SchedPolicy; +/* Assign thread tid to the cgroup associated with the specified policy. + * If the thread is a thread group leader, that is it's gettid() == getpid(), + * then the other threads in the same thread group are _not_ affected. + * On platforms which support gettid(), zero tid means current thread. + * Return value: 0 for success, or -errno for error. + */ extern int set_sched_policy(int tid, SchedPolicy policy); + +/* Return the policy associated with the cgroup of thread tid via policy pointer. + * On platforms which support gettid(), zero tid means current thread. + * Return value: 0 for success, or -1 for error and set errno. + */ extern int get_sched_policy(int tid, SchedPolicy *policy); +/* Return a displayable string corresponding to policy. + * Return value: non-NULL NUL-terminated name of unspecified length; + * the caller is responsible for displaying the useful part of the string. + */ +extern const char *get_sched_policy_name(SchedPolicy policy); + #ifdef __cplusplus } #endif diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h index cf103ca..36ac25d 100644 --- a/include/cutils/tztime.h +++ b/include/cutils/tztime.h @@ -26,8 +26,14 @@ extern "C" { time_t mktime_tz(struct tm * const tmp, char const * tz); void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz); -#ifndef HAVE_ANDROID_OS -/* the following is defined in <time.h> in Bionic */ +#ifdef HAVE_ANDROID_OS + +/* the following is defined in the Bionic C library on Android, but the + * declarations are only available through a platform-private header + */ +#include <bionic_time.h> + +#else /* !HAVE_ANDROID_OS */ struct strftime_locale { const char *mon[12]; /* short names */ diff --git a/include/cutils/uevent.h b/include/cutils/uevent.h index 4ebc300..4cca7e5 100644 --- a/include/cutils/uevent.h +++ b/include/cutils/uevent.h @@ -26,6 +26,7 @@ extern "C" { int uevent_open_socket(int buf_sz, bool passcred); ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length); +ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid); #ifdef __cplusplus } diff --git a/include/ion/ion.h b/include/ion/ion.h new file mode 100644 index 0000000..cafead5 --- /dev/null +++ b/include/ion/ion.h @@ -0,0 +1,40 @@ +/* + * ion.c + * + * Memory Allocator functions for ion + * + * Copyright 2011 Google, Inc + * + * 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 __SYS_CORE_ION_H +#define __SYS_CORE_ION_H + +#include <linux/ion.h> + +__BEGIN_DECLS + +int ion_open(); +int ion_close(int fd); +int ion_alloc(int fd, size_t len, size_t align, unsigned int flags, + struct ion_handle **handle); +int ion_free(int fd, struct ion_handle *handle); +int ion_map(int fd, struct ion_handle *handle, size_t length, int prot, + int flags, off_t offset, unsigned char **ptr, int *map_fd); +int ion_share(int fd, struct ion_handle *handle, int *share_fd); +int ion_import(int fd, int share_fd, struct ion_handle **handle); + +__END_DECLS + +#endif /* __SYS_CORE_ION_H */ diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h index bd16240..d25e58f 100644 --- a/include/netutils/dhcp.h +++ b/include/netutils/dhcp.h @@ -30,7 +30,8 @@ extern int dhcp_do_request(const char *ifname, char *dns1, char *dns2, char *server, - uint32_t *lease); + uint32_t *lease, + char *vendorInfo); extern int dhcp_stop(const char *ifname); extern int dhcp_release_lease(const char *ifname); extern char *dhcp_get_errmsg(); diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index ce1e55d..6521cbe 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -53,7 +53,7 @@ #define AID_KEYSTORE 1017 /* keystore subsystem */ #define AID_USB 1018 /* USB devices */ #define AID_DRM 1019 /* DRM server */ -#define AID_AVAILABLE 1020 /* available for use */ +#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */ #define AID_GPS 1021 /* GPS daemon */ #define AID_UNUSED1 1022 /* deprecated, DO NOT USE */ #define AID_MEDIA_RW 1023 /* internal media storage write access */ @@ -61,6 +61,7 @@ #define AID_UNUSED2 1025 /* deprecated, DO NOT USE */ #define AID_DRMRPC 1026 /* group for drm rpc */ #define AID_NFC 1027 /* nfc subsystem */ +#define AID_SDCARD_R 1028 /* external storage read access */ #define AID_SHELL 2000 /* adb and debug shell user */ #define AID_CACHE 2001 /* cache access */ @@ -79,7 +80,12 @@ #define AID_MISC 9998 /* access to misc storage */ #define AID_NOBODY 9999 -#define AID_APP 10000 /* first app user */ +#define AID_APP 10000 /* first app user */ + +#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */ +#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ + +#define AID_USER 100000 /* offset for uid ranges for each user */ #if !defined(EXCLUDE_FS_CONFIG_STRUCTURES) struct android_id_info { @@ -105,7 +111,7 @@ static const struct android_id_info android_ids[] = { { "install", AID_INSTALL, }, { "media", AID_MEDIA, }, { "drm", AID_DRM, }, - { "available", AID_AVAILABLE, }, + { "mdnsr", AID_MDNSR, }, { "nfc", AID_NFC, }, { "drmrpc", AID_DRMRPC, }, { "shell", AID_SHELL, }, @@ -113,6 +119,7 @@ static const struct android_id_info android_ids[] = { { "diag", AID_DIAG, }, { "net_bt_admin", AID_NET_BT_ADMIN, }, { "net_bt", AID_NET_BT, }, + { "sdcard_r", AID_SDCARD_R, }, { "sdcard_rw", AID_SDCARD_RW, }, { "media_rw", AID_MEDIA_RW, }, { "vpn", AID_VPN, }, @@ -131,7 +138,7 @@ static const struct android_id_info android_ids[] = { #define android_id_count \ (sizeof(android_ids) / sizeof(android_ids[0])) - + struct fs_path_config { unsigned mode; unsigned uid; @@ -217,6 +224,8 @@ static struct fs_path_config android_files[] = { { 00755, AID_ROOT, AID_ROOT, "bin/*" }, { 00750, AID_ROOT, AID_SHELL, "init*" }, { 00750, AID_ROOT, AID_SHELL, "charger*" }, + { 00750, AID_ROOT, AID_SHELL, "sbin/fs_mgr" }, + { 00640, AID_ROOT, AID_SHELL, "fstab.*" }, { 00644, AID_ROOT, AID_ROOT, 0 }, }; @@ -225,7 +234,7 @@ static inline void fs_config(const char *path, int dir, { struct fs_path_config *pc; int plen; - + pc = dir ? android_dirs : android_files; plen = strlen(path); for(; pc->prefix; pc++){ @@ -245,9 +254,9 @@ static inline void fs_config(const char *path, int dir, *uid = pc->uid; *gid = pc->gid; *mode = (*mode & (~07777)) | pc->mode; - + #if 0 - fprintf(stderr,"< '%s' '%s' %d %d %o >\n", + fprintf(stderr,"< '%s' '%s' %d %d %o >\n", path, pc->prefix ? pc->prefix : "", *uid, *gid, *mode); #endif } diff --git a/include/sync/sync.h b/include/sync/sync.h new file mode 100644 index 0000000..6aa5f2d --- /dev/null +++ b/include/sync/sync.h @@ -0,0 +1,45 @@ +/* + * sync.h + * + * Copyright 2012 Google, Inc + * + * 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 __SYS_CORE_SYNC_H +#define __SYS_CORE_SYNC_H + +#include <linux/sync.h> +#include <linux/sw_sync.h> + +__BEGIN_DECLS + +/* timeout in msecs */ +int sync_wait(int fd, unsigned int timeout); +int sync_merge(const char *name, int fd1, int fd2); +struct sync_fence_info_data *sync_fence_info(int fd); +struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info, + struct sync_pt_info *itr); +void sync_fence_info_free(struct sync_fence_info_data *info); + +/* sw_sync is mainly inteded for testing and should not be complied into + * production kernels + */ + +int sw_sync_timeline_create(void); +int sw_sync_timeline_inc(int fd, unsigned count); +int sw_sync_fence_create(int fd, const char *name, unsigned value); + +__END_DECLS + +#endif /* __SYS_CORE_SYNC_H */ diff --git a/include/system/audio.h b/include/system/audio.h index 52ba5e7..3807317 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -250,7 +250,9 @@ typedef enum { AUDIO_CHANNEL_IN_Z_AXIS | AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK), -} audio_channels_t; +}; + +typedef uint32_t audio_channel_mask_t; typedef enum { AUDIO_MODE_INVALID = -2, @@ -288,6 +290,8 @@ typedef enum { AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, + AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, + AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, AUDIO_DEVICE_OUT_DEFAULT = 0x8000, AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | @@ -302,6 +306,8 @@ typedef enum { AUDIO_DEVICE_OUT_AUX_DIGITAL | AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | + AUDIO_DEVICE_OUT_USB_ACCESSORY | + AUDIO_DEVICE_OUT_USB_DEVICE | AUDIO_DEVICE_OUT_DEFAULT), AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | @@ -309,6 +315,8 @@ typedef enum { AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO | AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), + AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY | + AUDIO_DEVICE_OUT_USB_DEVICE), /* input devices */ AUDIO_DEVICE_IN_COMMUNICATION = 0x10000, @@ -333,6 +341,29 @@ typedef enum { AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, } audio_devices_t; +/* the audio output flags serve two purposes: + * - when an AudioTrack is created they indicate a "wish" to be connected to an + * output stream with attributes corresponding to the specified flags + * - when present in an output profile descriptor listed for a particular audio + * hardware module, they indicate that an output stream can be opened that + * supports the attributes indicated by the flags. + * the audio policy manager will try to match the flags in the request + * (when getOuput() is called) to an available output stream. + */ +typedef enum { + AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes + AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track + // to one output stream: no software mixer + AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of + // the device. It is unique and must be + // present. It is opened by default and + // receives routing, audio mode and volume + // controls related to voice calls. + AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", + // defined elsewhere + AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8 // use deep audio buffers +} audio_output_flags_t; + static inline bool audio_is_output_device(audio_devices_t device) { if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) @@ -366,6 +397,14 @@ static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) return false; } +static inline bool audio_is_usb_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)) + return true; + else + return false; +} + static inline bool audio_is_input_channel(uint32_t channel) { if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) @@ -382,7 +421,52 @@ static inline bool audio_is_output_channel(uint32_t channel) return false; } -static inline bool audio_is_valid_format(uint32_t format) +/* Derive an output channel mask from a channel count. + * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel + * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, + * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC + * for continuity with stereo. + * Returns the matching channel mask, or 0 if the number of channels exceeds that of the + * configurations for which a default channel mask is defined. + */ +static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) +{ + switch(channel_count) { + case 1: + return AUDIO_CHANNEL_OUT_MONO; + case 2: + return AUDIO_CHANNEL_OUT_STEREO; + case 3: + return (AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER); + case 4: // 4.0 + return AUDIO_CHANNEL_OUT_QUAD; + case 5: // 5.0 + return (AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER); + case 6: // 5.1 + return AUDIO_CHANNEL_OUT_5POINT1; + case 7: // 6.1 + return (AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER); + case 8: + return AUDIO_CHANNEL_OUT_7POINT1; + default: + return 0; + } +} + +/* Similar to above, but for input. Currently handles only mono and stereo. */ +static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count) +{ + switch (channel_count) { + case 1: + return AUDIO_CHANNEL_IN_MONO; + case 2: + return AUDIO_CHANNEL_IN_STEREO; + default: + return 0; + } +} + +static inline bool audio_is_valid_format(audio_format_t format) { switch (format & AUDIO_FORMAT_MAIN_MASK) { case AUDIO_FORMAT_PCM: @@ -403,28 +487,28 @@ static inline bool audio_is_valid_format(uint32_t format) } } -static inline bool audio_is_linear_pcm(uint32_t format) +static inline bool audio_is_linear_pcm(audio_format_t format) { return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); } -static inline size_t audio_bytes_per_sample(uint32_t format) +static inline size_t audio_bytes_per_sample(audio_format_t format) { size_t size = 0; switch (format) { - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_8_24_BIT: - size = sizeof(int32_t); - break; - case AUDIO_FORMAT_PCM_16_BIT: - size = sizeof(int16_t); - break; - case AUDIO_FORMAT_PCM_8_BIT: - size = sizeof(uint8_t); - break; - default: - break; + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_8_24_BIT: + size = sizeof(int32_t); + break; + case AUDIO_FORMAT_PCM_16_BIT: + size = sizeof(int16_t); + break; + case AUDIO_FORMAT_PCM_8_BIT: + size = sizeof(uint8_t); + break; + default: + break; } return size; } diff --git a/include/system/audio_policy.h b/include/system/audio_policy.h index 0194962..91b8e9c 100644 --- a/include/system/audio_policy.h +++ b/include/system/audio_policy.h @@ -30,17 +30,6 @@ __BEGIN_DECLS * frameworks/base/include/media/AudioSystem.h */ -/* request to open a direct output with get_output() (by opposition to - * sharing an output with other AudioTracks) - */ -typedef enum { - AUDIO_POLICY_OUTPUT_FLAG_INDIRECT = 0x0, - AUDIO_POLICY_OUTPUT_FLAG_DIRECT = 0x1 -} audio_policy_output_flags_t; - -/* for future compatibility */ -typedef audio_policy_output_flags_t audio_output_flags_t; - /* device categories used for audio_policy->set_force_use() */ typedef enum { AUDIO_POLICY_FORCE_NONE, @@ -53,6 +42,7 @@ typedef enum { AUDIO_POLICY_FORCE_BT_DESK_DOCK, AUDIO_POLICY_FORCE_ANALOG_DOCK, AUDIO_POLICY_FORCE_DIGITAL_DOCK, + AUDIO_POLICY_FORCE_NO_BT_A2DP, /* A2DP sink is not preferred to speaker or wired HS */ AUDIO_POLICY_FORCE_CFG_CNT, AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, diff --git a/include/system/camera.h b/include/system/camera.h index cdfa256..e4cacc5 100644 --- a/include/system/camera.h +++ b/include/system/camera.h @@ -85,6 +85,9 @@ enum { // request FRAME and METADATA. Or the apps can request only FRAME or only // METADATA. CAMERA_MSG_PREVIEW_METADATA = 0x0400, // dataCallback + // Notify on autofocus start and stop. This is useful in continuous + // autofocus - FOCUS_MODE_CONTINUOUS_VIDEO and FOCUS_MODE_CONTINUOUS_PICTURE. + CAMERA_MSG_FOCUS_MOVE = 0x0800, // notifyCallback CAMERA_MSG_ALL_MSGS = 0xFFFF }; @@ -134,7 +137,8 @@ enum { * KEY_FOCUS_AREAS and KEY_METERING_AREAS have no effect. * * arg1 is the face detection type. It can be CAMERA_FACE_DETECTION_HW or - * CAMERA_FACE_DETECTION_SW. + * CAMERA_FACE_DETECTION_SW. If the type of face detection requested is not + * supported, the HAL must return BAD_VALUE. */ CAMERA_CMD_START_FACE_DETECTION = 6, @@ -142,11 +146,36 @@ enum { * Stop the face detection. */ CAMERA_CMD_STOP_FACE_DETECTION = 7, + + /** + * Enable/disable focus move callback (CAMERA_MSG_FOCUS_MOVE). Passing + * arg1 = 0 will disable, while passing arg1 = 1 will enable the callback. + */ + CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG = 8, + + /** + * Ping camera service to see if camera hardware is released. + * + * When any camera method returns error, the client can use ping command + * to see if the camera has been taken away by other clients. If the result + * is NO_ERROR, it means the camera hardware is not released. If the result + * is not NO_ERROR, the camera has been released and the existing client + * can silently finish itself or show a dialog. + */ + CAMERA_CMD_PING = 9, }; /** camera fatal errors */ enum { CAMERA_ERROR_UNKNOWN = 1, + /** + * Camera was released because another client has connected to the camera. + * The original client should call Camera::disconnect immediately after + * getting this notification. Otherwise, the camera will be released by + * camera service in a short time. The client should not call any method + * (except disconnect and sending CAMERA_CMD_PING) after getting this. + */ + CAMERA_ERROR_RELEASED = 2, CAMERA_ERROR_SERVER_DIED = 100 }; diff --git a/include/system/graphics.h b/include/system/graphics.h index d42a0d3..24e2bfb 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -78,7 +78,8 @@ enum { * - a vertical stride equal to the height * * y_size = stride * height - * c_size = ALIGN(stride/2, 16) * height/2 + * c_stride = ALIGN(stride/2, 16) + * c_size = c_stride * height/2 * size = y_size + c_size * 2 * cr_offset = y_size * cb_offset = y_size + c_size @@ -86,7 +87,26 @@ enum { */ HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar - /* Forward compatibility */ + /* + * Android RAW sensor format: + * + * This format is exposed outside of the HAL to applications. + * + * RAW_SENSOR is a single-channel 16-bit format, typically representing raw + * Bayer-pattern images from an image sensor, with minimal processing. + * + * The exact pixel layout of the data in the buffer is sensor-dependent, and + * needs to be queried from the camera device. + * + * Generally, not all 16 bits are used; more common values are 10 or 12 + * bits. All parameters to interpret the raw data (black and white points, + * color space, etc) must be queried from the camera device. + * + * This format assumes + * - an even width + * - an even height + * - a horizontal stride multiple of 16 pixels (32 bytes). + */ HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, /* Legacy formats (deprecated), used by ImageFormat.java */ diff --git a/include/system/window.h b/include/system/window.h index 9602445..8e00bcd 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -157,9 +157,10 @@ enum { /* - * Default width and height of the ANativeWindow, these are the dimensions - * of the window irrespective of the NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS - * call. + * Default width and height of ANativeWindow buffers, these are the + * dimensions of the window buffers irrespective of the + * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS call and match the native window + * size unless overridden by NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS. */ NATIVE_WINDOW_DEFAULT_WIDTH = 6, NATIVE_WINDOW_DEFAULT_HEIGHT = 7, @@ -212,25 +213,42 @@ enum { * */ NATIVE_WINDOW_TRANSFORM_HINT = 8, + + /* + * Boolean that indicates whether the consumer is running more than + * one buffer behind the producer. + */ + NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9 }; -/* valid operations for the (*perform)() hook */ +/* Valid operations for the (*perform)() hook. + * + * Values marked as 'deprecated' are supported, but have been superceded by + * other functionality. + * + * Values marked as 'private' should be considered private to the framework. + * HAL implementation code with access to an ANativeWindow should not use these, + * as it may not interact properly with the framework's use of the + * ANativeWindow. + */ enum { NATIVE_WINDOW_SET_USAGE = 0, NATIVE_WINDOW_CONNECT = 1, /* deprecated */ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ - NATIVE_WINDOW_SET_CROP = 3, + NATIVE_WINDOW_SET_CROP = 3, /* private */ NATIVE_WINDOW_SET_BUFFER_COUNT = 4, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */ NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7, NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8, NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9, - NATIVE_WINDOW_SET_SCALING_MODE = 10, + NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */ NATIVE_WINDOW_LOCK = 11, /* private */ NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */ NATIVE_WINDOW_API_CONNECT = 13, /* private */ NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ + NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */ + NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* private */ }; /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ @@ -276,6 +294,15 @@ enum { NATIVE_WINDOW_SCALING_MODE_FREEZE = 0, /* the buffer is scaled in both dimensions to match the window size */ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1, + /* the buffer is scaled uniformly such that the smaller dimension + * of the buffer matches the window size (cropping in the process) + */ + NATIVE_WINDOW_SCALING_MODE_SCALE_CROP = 2, + /* the window is clipped to the size of the buffer's crop rectangle; pixels + * outside the crop rectangle are treated as if they are completely + * transparent. + */ + NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP = 3, }; /* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */ @@ -405,18 +432,20 @@ struct ANativeWindow * NATIVE_WINDOW_SET_USAGE * NATIVE_WINDOW_CONNECT (deprecated) * NATIVE_WINDOW_DISCONNECT (deprecated) - * NATIVE_WINDOW_SET_CROP + * NATIVE_WINDOW_SET_CROP (private) * NATIVE_WINDOW_SET_BUFFER_COUNT * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY (deprecated) * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM * NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP * NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS * NATIVE_WINDOW_SET_BUFFERS_FORMAT - * NATIVE_WINDOW_SET_SCALING_MODE + * NATIVE_WINDOW_SET_SCALING_MODE (private) * NATIVE_WINDOW_LOCK (private) * NATIVE_WINDOW_UNLOCK_AND_POST (private) * NATIVE_WINDOW_API_CONNECT (private) * NATIVE_WINDOW_API_DISCONNECT (private) + * NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS (private) + * NATIVE_WINDOW_SET_POST_TRANSFORM_CROP (private) * */ @@ -480,14 +509,16 @@ static inline int native_window_disconnect( /* * native_window_set_crop(..., crop) * Sets which region of the next queued buffers needs to be considered. - * A buffer's crop region is scaled to match the surface's size. + * Depending on the scaling mode, a buffer's crop region is scaled and/or + * cropped to match the surface's size. This function sets the crop in + * pre-transformed buffer pixel coordinates. * * The specified crop region applies to all buffers queued after it is called. * - * if 'crop' is NULL, subsequently queued buffers won't be cropped. + * If 'crop' is NULL, subsequently queued buffers won't be cropped. * - * An error is returned if for instance the crop region is invalid, - * out of the buffer's bound or if the window is invalid. + * An error is returned if for instance the crop region is invalid, out of the + * buffer's bound or if the window is invalid. */ static inline int native_window_set_crop( struct ANativeWindow* window, @@ -497,6 +528,41 @@ static inline int native_window_set_crop( } /* + * native_window_set_post_transform_crop(..., crop) + * Sets which region of the next queued buffers needs to be considered. + * Depending on the scaling mode, a buffer's crop region is scaled and/or + * cropped to match the surface's size. This function sets the crop in + * post-transformed pixel coordinates. + * + * The specified crop region applies to all buffers queued after it is called. + * + * If 'crop' is NULL, subsequently queued buffers won't be cropped. + * + * An error is returned if for instance the crop region is invalid, out of the + * buffer's bound or if the window is invalid. + */ +static inline int native_window_set_post_transform_crop( + struct ANativeWindow* window, + android_native_rect_t const * crop) +{ + return window->perform(window, NATIVE_WINDOW_SET_POST_TRANSFORM_CROP, crop); +} + +/* + * native_window_set_active_rect(..., active_rect) + * + * This function is deprectated and will be removed soon. For now it simply + * sets the post-transform crop for compatibility while multi-project commits + * get checked. + */ +static inline int native_window_set_active_rect( + struct ANativeWindow* window, + android_native_rect_t const * active_rect) +{ + return native_window_set_post_transform_crop(window, active_rect); +} + +/* * native_window_set_buffer_count(..., count) * Sets the number of buffers associated with this native window. */ @@ -527,7 +593,7 @@ static inline int native_window_set_buffers_geometry( /* * native_window_set_buffers_dimensions(..., int w, int h) * All buffers dequeued after this call will have the dimensions specified. - * In particular, all buffers will have a fixed-size, independent form the + * In particular, all buffers will have a fixed-size, independent from the * native-window size. They will be scaled according to the scaling mode * (see native_window_set_scaling_mode) upon window composition. * @@ -546,6 +612,31 @@ static inline int native_window_set_buffers_dimensions( } /* + * native_window_set_buffers_user_dimensions(..., int w, int h) + * + * Sets the user buffer size for the window, which overrides the + * window's size. All buffers dequeued after this call will have the + * dimensions specified unless overridden by + * native_window_set_buffers_dimensions. All buffers will have a + * fixed-size, independent from the native-window size. They will be + * scaled according to the scaling mode (see + * native_window_set_scaling_mode) upon window composition. + * + * If w and h are 0, the normal behavior is restored. That is, the + * default buffer size will match the windows's size. + * + * Calling this function will reset the window crop to a NULL value, which + * disables cropping of the buffers. + */ +static inline int native_window_set_buffers_user_dimensions( + struct ANativeWindow* window, + int w, int h) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS, + w, h); +} + +/* * native_window_set_buffers_format(..., int format) * All buffers dequeued after this call will have the format specified. * @@ -602,7 +693,6 @@ static inline int native_window_set_scaling_mode( mode); } - /* * native_window_api_connect(..., int api) * connects an API to this window. only one API can be connected at a time. diff --git a/include/sysutils/FrameworkClient.h b/include/sysutils/FrameworkClient.h index 0ef0753..4a3f0de 100644 --- a/include/sysutils/FrameworkClient.h +++ b/include/sysutils/FrameworkClient.h @@ -1,7 +1,7 @@ #ifndef _FRAMEWORK_CLIENT_H #define _FRAMEWORK_CLIENT_H -#include "../../../frameworks/base/include/utils/List.h" +#include "List.h" #include <pthread.h> @@ -17,5 +17,5 @@ public: int sendMsg(const char *msg, const char *data); }; -typedef android::List<FrameworkClient *> FrameworkClientCollection; +typedef android::sysutils::List<FrameworkClient *> FrameworkClientCollection; #endif diff --git a/include/sysutils/FrameworkCommand.h b/include/sysutils/FrameworkCommand.h index 6c1fca6..3e6264b 100644 --- a/include/sysutils/FrameworkCommand.h +++ b/include/sysutils/FrameworkCommand.h @@ -16,7 +16,7 @@ #ifndef __FRAMEWORK_CMD_HANDLER_H #define __FRAMEWORK_CMD_HANDLER_H -#include "../../../frameworks/base/include/utils/List.h" +#include "List.h" class SocketClient; @@ -34,5 +34,5 @@ public: const char *getCommand() { return mCommand; } }; -typedef android::List<FrameworkCommand *> FrameworkCommandCollection; +typedef android::sysutils::List<FrameworkCommand *> FrameworkCommandCollection; #endif diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h index 142f50c..756bacf 100644 --- a/include/sysutils/FrameworkListener.h +++ b/include/sysutils/FrameworkListener.h @@ -24,11 +24,17 @@ class SocketClient; class FrameworkListener : public SocketListener { public: static const int CMD_ARGS_MAX = 16; + + /* 1 out of errorRate will be dropped */ + int errorRate; private: + int mCommandCount; + bool mWithSeq; FrameworkCommandCollection *mCommands; public: FrameworkListener(const char *socketName); + FrameworkListener(const char *socketName, bool withSeq); virtual ~FrameworkListener() {} protected: @@ -37,5 +43,6 @@ protected: private: void dispatchCommand(SocketClient *c, char *data); + void init(const char *socketName, bool withSeq); }; #endif diff --git a/include/sysutils/List.h b/include/sysutils/List.h new file mode 100644 index 0000000..31f7b37 --- /dev/null +++ b/include/sysutils/List.h @@ -0,0 +1,334 @@ +/* + * 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. + */ + +// +// Templated list class. Normally we'd use STL, but we don't have that. +// This class mimics STL's interfaces. +// +// Objects are copied into the list with the '=' operator or with copy- +// construction, so if the compiler's auto-generated versions won't work for +// you, define your own. +// +// The only class you want to use from here is "List". +// +#ifndef _SYSUTILS_LIST_H +#define _SYSUTILS_LIST_H + +#include <stddef.h> +#include <stdint.h> + +namespace android { +namespace sysutils { + +/* + * Doubly-linked list. Instantiate with "List<MyClass> myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template<typename T> +class List +{ +protected: + /* + * One element in the list. + */ + class _Node { + public: + explicit _Node(const T& val) : mVal(val) {} + ~_Node() {} + inline T& getRef() { return mVal; } + inline const T& getRef() const { return mVal; } + inline _Node* getPrev() const { return mpPrev; } + inline _Node* getNext() const { return mpNext; } + inline void setVal(const T& val) { mVal = val; } + inline void setPrev(_Node* ptr) { mpPrev = ptr; } + inline void setNext(_Node* ptr) { mpNext = ptr; } + private: + friend class List; + friend class _ListIterator; + T mVal; + _Node* mpPrev; + _Node* mpNext; + }; + + /* + * Iterator for walking through the list. + */ + + template <typename TYPE> + struct CONST_ITERATOR { + typedef _Node const * NodePtr; + typedef const TYPE Type; + }; + + template <typename TYPE> + struct NON_CONST_ITERATOR { + typedef _Node* NodePtr; + typedef TYPE Type; + }; + + template< + typename U, + template <class> class Constness + > + class _ListIterator { + typedef _ListIterator<U, Constness> _Iter; + typedef typename Constness<U>::NodePtr _NodePtr; + typedef typename Constness<U>::Type _Type; + + explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {} + + public: + _ListIterator() {} + _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {} + ~_ListIterator() {} + + // this will handle conversions from iterator to const_iterator + // (and also all convertible iterators) + // Here, in this implementation, the iterators can be converted + // if the nodes can be converted + template<typename V> explicit + _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {} + + + /* + * Dereference operator. Used to get at the juicy insides. + */ + _Type& operator*() const { return mpNode->getRef(); } + _Type* operator->() const { return &(mpNode->getRef()); } + + /* + * Iterator comparison. + */ + inline bool operator==(const _Iter& right) const { + return mpNode == right.mpNode; } + + inline bool operator!=(const _Iter& right) const { + return mpNode != right.mpNode; } + + /* + * handle comparisons between iterator and const_iterator + */ + template<typename OTHER> + inline bool operator==(const OTHER& right) const { + return mpNode == right.mpNode; } + + template<typename OTHER> + inline bool operator!=(const OTHER& right) const { + return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + inline _Iter& operator++() { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + const _Iter operator++(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getNext(); + return tmp; + } + inline _Iter& operator--() { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + const _Iter operator--(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getPrev(); + return tmp; + } + + inline _NodePtr getNode() const { return mpNode; } + + _NodePtr mpNode; /* should be private, but older gcc fails */ + private: + friend class List; + }; + +public: + List() { + prep(); + } + List(const List<T>& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List() { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator<T, NON_CONST_ITERATOR> iterator; + typedef _ListIterator<T, CONST_ITERATOR> const_iterator; + + List<T>& operator=(const List<T>& right); + + /* returns true if the list is empty */ + inline bool empty() const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + size_t size() const { + return size_t(distance(begin(), end())); + } + + /* + * Return the first element or one past the last element. The + * _Node* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + inline iterator begin() { + return iterator(mpMiddle->getNext()); + } + inline const_iterator begin() const { + return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); + } + inline iterator end() { + return iterator(mpMiddle); + } + inline const_iterator end() const { + return const_iterator(const_cast<_Node const*>(mpMiddle)); + } + + /* add the object to the head or tail of the list */ + void push_front(const T& val) { insert(begin(), val); } + void push_back(const T& val) { insert(end(), val); } + + /* insert before the current node; returns iterator at new node */ + iterator insert(iterator posn, const T& val) + { + _Node* newNode = new _Node(val); // alloc & copy-construct + newNode->setNext(posn.getNode()); + newNode->setPrev(posn.getNode()->getPrev()); + posn.getNode()->getPrev()->setNext(newNode); + posn.getNode()->setPrev(newNode); + return iterator(newNode); + } + + /* insert a range of elements before the current node */ + void insert(iterator posn, const_iterator first, const_iterator last) { + for ( ; first != last; ++first) + insert(posn, *first); + } + + /* remove one entry; returns iterator at next node */ + iterator erase(iterator posn) { + _Node* pNext = posn.getNode()->getNext(); + _Node* pPrev = posn.getNode()->getPrev(); + pPrev->setNext(pNext); + pNext->setPrev(pPrev); + delete posn.getNode(); + return iterator(pNext); + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return iterator(last); + } + + /* remove all contents of the list */ + void clear() { + _Node* pCurrent = mpMiddle->getNext(); + _Node* pNext; + + while (pCurrent != mpMiddle) { + pNext = pCurrent->getNext(); + delete pCurrent; + pCurrent = pNext; + } + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * Measure the distance between two iterators. On exist, "first" + * will be equal to "last". The iterators must refer to the same + * list. + * + * FIXME: This is actually a generic iterator function. It should be a + * template function at the top-level with specializations for things like + * vector<>, which can just do pointer math). Here we limit it to + * _ListIterator of the same type but different constness. + */ + template< + typename U, + template <class> class CL, + template <class> class CR + > + ptrdiff_t distance( + _ListIterator<U, CL> first, _ListIterator<U, CR> last) const + { + ptrdiff_t count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _Node but don't need it to hold valid data. More + * to the point, I don't want T's constructor to fire, since it + * might have side-effects or require arguments. So, we do this + * slightly uncouth storage alloc. + */ + void prep() { + mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * This node plays the role of "pointer to head" and "pointer to tail". + * It sits in the middle of a circular list of nodes. The iterator + * runs around the circle until it encounters this one. + */ + _Node* mpMiddle; +}; + +/* + * Assignment operator. + * + * The simplest way to do this would be to clear out the target list and + * fill it with the source. However, we can speed things along by + * re-using existing elements. + */ +template<class T> +List<T>& List<T>::operator=(const List<T>& right) +{ + if (this == &right) + return *this; // self-assignment + iterator firstDst = begin(); + iterator lastDst = end(); + const_iterator firstSrc = right.begin(); + const_iterator lastSrc = right.end(); + while (firstSrc != lastSrc && firstDst != lastDst) + *firstDst++ = *firstSrc++; + if (firstSrc == lastSrc) // ran out of elements in source? + erase(firstDst, lastDst); // yes, erase any extras + else + insert(lastDst, firstSrc, lastSrc); // copy remaining over + return *this; +} + +}; // namespace sysutils +}; // namespace android + +#endif // _SYSUTILS_LIST_H diff --git a/include/sysutils/SocketClient.h b/include/sysutils/SocketClient.h index 7d2b1d6..85b58ef 100644 --- a/include/sysutils/SocketClient.h +++ b/include/sysutils/SocketClient.h @@ -1,9 +1,10 @@ #ifndef _SOCKET_CLIENT_H #define _SOCKET_CLIENT_H -#include "../../../frameworks/base/include/utils/List.h" +#include "List.h" #include <pthread.h> +#include <cutils/atomic.h> #include <sys/types.h> class SocketClient { @@ -24,18 +25,34 @@ class SocketClient { pthread_mutex_t mRefCountMutex; int mRefCount; + int mCmdNum; + + bool mUseCmdNum; + public: SocketClient(int sock, bool owned); + SocketClient(int sock, bool owned, bool useCmdNum); virtual ~SocketClient(); int getSocket() { return mSocket; } pid_t getPid() const { return mPid; } uid_t getUid() const { return mUid; } gid_t getGid() const { return mGid; } + void setCmdNum(int cmdNum) { android_atomic_release_store(cmdNum, &mCmdNum); } + int getCmdNum() { return mCmdNum; } // Send null-terminated C strings: int sendMsg(int code, const char *msg, bool addErrno); - int sendMsg(const char *msg); + int sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum); + + // Provides a mechanism to send a response code to the client. + // Sends the code and a null character. + int sendCode(int code); + + // Provides a mechanism to send binary data to client. + // Sends the code and a null character, followed by 4 bytes of + // big-endian length, and the data. + int sendBinaryMsg(int code, const void *data, int len); // Sending binary data: int sendData(const void *data, int len); @@ -46,7 +63,21 @@ public: // decRef() when it's done with the client. void incRef(); bool decRef(); // returns true at 0 (but note: SocketClient already deleted) + + // return a new string in quotes with '\\' and '\"' escaped for "my arg" transmissions + static char *quoteArg(const char *arg); + +private: + // Send null-terminated C strings + int sendMsg(const char *msg); + void init(int socket, bool owned, bool useCmdNum); + + // Sending binary data. The caller should use make sure this is protected + // from multiple threads entering simultaneously. + // returns 0 if successful, -1 if there is a 0 byte write and -2 if any other + // error occurred (use errno to get the error) + int sendDataLocked(const void *data, int len); }; -typedef android::List<SocketClient *> SocketClientCollection; +typedef android::sysutils::List<SocketClient *> SocketClientCollection; #endif diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h index 6592b01..8f56230 100644 --- a/include/sysutils/SocketListener.h +++ b/include/sysutils/SocketListener.h @@ -21,16 +21,18 @@ #include <sysutils/SocketClient.h> class SocketListener { - int mSock; + bool mListen; const char *mSocketName; + int mSock; SocketClientCollection *mClients; pthread_mutex_t mClientsLock; - bool mListen; int mCtrlPipe[2]; pthread_t mThread; + bool mUseCmdNum; public: SocketListener(const char *socketName, bool listen); + SocketListener(const char *socketName, bool listen, bool useCmdNum); SocketListener(int socketFd, bool listen); virtual ~SocketListener(); @@ -38,7 +40,6 @@ public: int stopListener(); void sendBroadcast(int code, const char *msg, bool addErrno); - void sendBroadcast(const char *msg); protected: virtual bool onDataAvailable(SocketClient *c) = 0; @@ -46,5 +47,6 @@ protected: private: static void *threadStart(void *obj); void runListener(); + void init(const char *socketName, int socketFd, bool listen, bool useCmdNum); }; #endif diff --git a/init/Android.mk b/init/Android.mk index e9fd884..7dae9df 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -22,17 +22,21 @@ LOCAL_SRC_FILES += bootchart.c LOCAL_CFLAGS += -DBOOTCHART=1 endif +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1 +endif + LOCAL_MODULE:= init LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils libc ifeq ($(HAVE_SELINUX),true) LOCAL_STATIC_LIBRARIES += libselinux -LOCAL_C_INCLUDES := external/libselinux/include +LOCAL_C_INCLUDES += external/libselinux/include LOCAL_CFLAGS += -DHAVE_SELINUX endif diff --git a/init/builtins.c b/init/builtins.c index a05ca45..5bda7a0 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -29,9 +29,11 @@ #include <stdlib.h> #include <sys/mount.h> #include <sys/resource.h> +#include <sys/wait.h> #include <linux/loop.h> #include <cutils/partition_utils.h> #include <sys/system_properties.h> +#include <fs_mgr.h> #ifdef HAVE_SELINUX #include <selinux/selinux.h> @@ -433,70 +435,69 @@ int do_mount(int nargs, char **args) if (wait) wait_for_file(source, COMMAND_RETRY_TIMEOUT); if (mount(source, target, system, flags, options) < 0) { - /* If this fails, it may be an encrypted filesystem - * or it could just be wiped. If wiped, that will be - * handled later in the boot process. - * We only support encrypting /data. Check - * if we're trying to mount it, and if so, - * assume it's encrypted, mount a tmpfs instead. - * Then save the orig mount parms in properties - * for vold to query when it mounts the real - * encrypted /data. - */ - if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) { - const char *tmpfs_options; - - tmpfs_options = property_get("ro.crypto.tmpfs_options"); - - if (mount("tmpfs", target, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV, - tmpfs_options) < 0) { - return -1; - } - - /* Set the property that triggers the framework to do a minimal - * startup and ask the user for a password - */ - property_set("ro.crypto.state", "encrypted"); - property_set("vold.decrypt", "1"); - } else { - return -1; - } + return -1; } - if (!strcmp(target, DATA_MNT_POINT)) { - char fs_flags[32]; - - /* Save the original mount options */ - property_set("ro.crypto.fs_type", system); - property_set("ro.crypto.fs_real_blkdev", source); - property_set("ro.crypto.fs_mnt_point", target); - if (options) { - property_set("ro.crypto.fs_options", options); - } - snprintf(fs_flags, sizeof(fs_flags), "0x%8.8x", flags); - property_set("ro.crypto.fs_flags", fs_flags); - } } exit_success: - /* If not running encrypted, then set the property saying we are - * unencrypted, and also trigger the action for a nonencrypted system. - */ - if (!strcmp(target, DATA_MNT_POINT)) { - const char *prop; + return 0; - prop = property_get("ro.crypto.state"); - if (! prop) { - prop = "notset"; +} + +int do_mount_all(int nargs, char **args) +{ + pid_t pid; + int ret = -1; + int child_ret = -1; + int status; + const char *prop; + + if (nargs != 2) { + return -1; + } + + /* + * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and + * do the call in the child to provide protection to the main init + * process if anything goes wrong (crash or memory leak), and wait for + * the child to finish in the parent. + */ + pid = fork(); + if (pid > 0) { + /* Parent. Wait for the child to return */ + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + } else { + ret = -1; } - if (strcmp(prop, "encrypted")) { - property_set("ro.crypto.state", "unencrypted"); - action_for_each_trigger("nonencrypted", action_add_queue_tail); + } else if (pid == 0) { + /* child, call fs_mgr_mount_all() */ + klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */ + child_ret = fs_mgr_mount_all(args[1]); + if (child_ret == -1) { + ERROR("fs_mgr_mount_all returned an error\n"); } + exit(child_ret); + } else { + /* fork failed, return an error */ + return -1; } - return 0; + /* ret is 1 if the device is encrypted, 0 if not, and -1 on error */ + if (ret == 1) { + property_set("ro.crypto.state", "encrypted"); + property_set("vold.decrypt", "1"); + } else if (ret == 0) { + property_set("ro.crypto.state", "unencrypted"); + /* If fs_mgr determined this is an unencrypted device, then trigger + * that action. + */ + action_for_each_trigger("nonencrypted", action_add_queue_tail); + } + return ret; } int do_setcon(int nargs, char **args) { diff --git a/init/devices.c b/init/devices.c index 1394351..c367de8 100644 --- a/init/devices.c +++ b/init/devices.c @@ -63,6 +63,7 @@ struct uevent { const char *subsystem; const char *firmware; const char *partition_name; + const char *device_name; int partition_num; int major; int minor; @@ -309,6 +310,7 @@ static void parse_event(const char *msg, struct uevent *uevent) uevent->minor = -1; uevent->partition_name = NULL; uevent->partition_num = -1; + uevent->device_name = NULL; /* currently ignoring SEQNUM */ while(*msg) { @@ -336,9 +338,12 @@ static void parse_event(const char *msg, struct uevent *uevent) } else if(!strncmp(msg, "PARTNAME=", 9)) { msg += 9; uevent->partition_name = msg; + } else if(!strncmp(msg, "DEVNAME=", 8)) { + msg += 8; + uevent->device_name = msg; } - /* advance to after the next \0 */ + /* advance to after the next \0 */ while(*msg++) ; } @@ -553,18 +558,39 @@ static void handle_generic_device_event(struct uevent *uevent) if (!strncmp(uevent->subsystem, "usb", 3)) { if (!strcmp(uevent->subsystem, "usb")) { - /* This imitates the file system that would be created - * if we were using devfs instead. - * Minors are broken up into groups of 128, starting at "001" - */ - int bus_id = uevent->minor / 128 + 1; - int device_id = uevent->minor % 128 + 1; - /* build directories */ - make_dir("/dev/bus", 0755); - make_dir("/dev/bus/usb", 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); - make_dir(devpath, 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + if (uevent->device_name) { + /* + * create device node provided by kernel if present + * see drivers/base/core.c + */ + char *p = devpath; + snprintf(devpath, sizeof(devpath), "/dev/%s", uevent->device_name); + /* skip leading /dev/ */ + p += 5; + /* build directories */ + while (*p) { + if (*p == '/') { + *p = 0; + make_dir(devpath, 0755); + *p = '/'; + } + p++; + } + } + else { + /* This imitates the file system that would be created + * if we were using devfs instead. + * Minors are broken up into groups of 128, starting at "001" + */ + int bus_id = uevent->minor / 128 + 1; + int device_id = uevent->minor % 128 + 1; + /* build directories */ + make_dir("/dev/bus", 0755); + make_dir("/dev/bus/usb", 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); + make_dir(devpath, 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + } } else { /* ignore other USB events */ return; diff --git a/init/init.c b/init/init.c index 31f5c12..cc98afc 100755 --- a/init/init.c +++ b/init/init.c @@ -134,6 +134,7 @@ static void open_console() if ((fd = open(console_name, O_RDWR)) < 0) { fd = open("/dev/null", O_RDWR); } + ioctl(fd, TIOCSCTTY, 0); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); @@ -239,6 +240,7 @@ void service_start(struct service *svc, const char *dynamic_args) char tmp[32]; int fd, sz; + umask(077); if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); @@ -646,7 +648,6 @@ static void export_kernel_boot_props(void) { "ro.boot.serialno", "ro.serialno", "", }, { "ro.boot.mode", "ro.bootmode", "unknown", }, { "ro.boot.baseband", "ro.baseband", "unknown", }, - { "ro.boot.carrier", "ro.carrier", "unknown", }, { "ro.boot.bootloader", "ro.bootloader", "unknown", }, }; diff --git a/init/init_parser.c b/init/init_parser.c index f538450..5393e52 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -122,6 +122,7 @@ int lookup_keyword(const char *s) break; case 'm': if (!strcmp(s, "kdir")) return K_mkdir; + if (!strcmp(s, "ount_all")) return K_mount_all; if (!strcmp(s, "ount")) return K_mount; break; case 'o': diff --git a/init/keywords.h b/init/keywords.h index 307c084..97d4950 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -12,6 +12,7 @@ int do_hostname(int nargs, char **args); int do_ifup(int nargs, char **args); int do_insmod(int nargs, char **args); int do_mkdir(int nargs, char **args); +int do_mount_all(int nargs, char **args); int do_mount(int nargs, char **args); int do_restart(int nargs, char **args); int do_restorecon(int nargs, char **args); @@ -60,6 +61,7 @@ enum { KEYWORD(import, SECTION, 1, 0) KEYWORD(keycodes, OPTION, 0, 0) KEYWORD(mkdir, COMMAND, 1, do_mkdir) + KEYWORD(mount_all, COMMAND, 1, do_mount_all) KEYWORD(mount, COMMAND, 3, do_mount) KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) diff --git a/init/property_service.c b/init/property_service.c index 059db97..79914cd 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -78,6 +78,7 @@ struct { { "wlan.", AID_SYSTEM, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, + { "debug.", AID_SYSTEM, 0 }, { "debug.", AID_SHELL, 0 }, { "log.", AID_SHELL, 0 }, { "service.adb.root", AID_SHELL, 0 }, @@ -338,21 +339,6 @@ int property_set(const char *name, const char *value) return 0; } -static int property_list(void (*propfn)(const char *key, const char *value, void *cookie), - void *cookie) -{ - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; - const prop_info *pi; - unsigned n; - - for(n = 0; (pi = __system_property_find_nth(n)); n++) { - __system_property_read(pi, name, value); - propfn(name, value, cookie); - } - return 0; -} - void handle_property_set_fd() { prop_msg msg; @@ -527,7 +513,9 @@ int properties_inited(void) */ void load_persist_props(void) { +#ifdef ALLOW_LOCAL_PROP_OVERRIDE load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); +#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); } @@ -538,7 +526,9 @@ void start_property_service(void) load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); +#ifdef ALLOW_LOCAL_PROP_OVERRIDE load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); +#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); diff --git a/init/ueventd.c b/init/ueventd.c index ecf3b9b..a89e067 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -53,11 +53,18 @@ int ueventd_main(int argc, char **argv) int nr; char tmp[32]; - /* Prevent fire-and-forget children from becoming zombies. - * If we should need to wait() for some children in the future - * (as opposed to none right now), double-forking here instead - * of ignoring SIGCHLD may be the better solution. - */ + /* + * init sets the umask to 077 for forked processes. We need to + * create files with exact permissions, without modification by + * the umask. + */ + umask(000); + + /* Prevent fire-and-forget children from becoming zombies. + * If we should need to wait() for some children in the future + * (as opposed to none right now), double-forking here instead + * of ignoring SIGCHLD may be the better solution. + */ signal(SIGCHLD, SIG_IGN); open_devnull_stdio(); diff --git a/init/util.c b/init/util.c index 8922de5..f994ab9 100755 --- a/init/util.c +++ b/init/util.c @@ -151,11 +151,23 @@ void *read_file(const char *fn, unsigned *_sz) char *data; int sz; int fd; + struct stat sb; data = 0; fd = open(fn, O_RDONLY); if(fd < 0) return 0; + // for security reasons, disallow world-writable + // or group-writable files + if (fstat(fd, &sb) < 0) { + ERROR("fstat failed for '%s'\n", fn); + goto oops; + } + if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) { + ERROR("skipping insecure file '%s'\n", fn); + goto oops; + } + sz = lseek(fd, 0, SEEK_END); if(sz < 0) goto oops; diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk new file mode 100644 index 0000000..433f93c --- /dev/null +++ b/libcorkscrew/Android.mk @@ -0,0 +1,46 @@ +# Copyright (C) 2011 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + backtrace.c \ + backtrace-helper.c \ + demangle.c \ + map_info.c \ + ptrace.c \ + symbol_table.c + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += \ + arch-arm/backtrace-arm.c \ + arch-arm/ptrace-arm.c +LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH +endif +ifeq ($(TARGET_ARCH),x86) +LOCAL_SRC_FILES += \ + arch-x86/backtrace-x86.c \ + arch-x86/ptrace-x86.c +LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH +endif + +LOCAL_SHARED_LIBRARIES += libdl libcutils libgccdemangle + +LOCAL_CFLAGS += -std=gnu99 -Werror +LOCAL_MODULE := libcorkscrew +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) diff --git a/libcorkscrew/MODULE_LICENSE_APACHE2 b/libcorkscrew/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libcorkscrew/MODULE_LICENSE_APACHE2 diff --git a/libcorkscrew/NOTICE b/libcorkscrew/NOTICE new file mode 100644 index 0000000..becc120 --- /dev/null +++ b/libcorkscrew/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2011, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libcorkscrew/arch-arm/backtrace-arm.c b/libcorkscrew/arch-arm/backtrace-arm.c new file mode 100644 index 0000000..5b91164 --- /dev/null +++ b/libcorkscrew/arch-arm/backtrace-arm.c @@ -0,0 +1,589 @@ +/* + * Copyright (C) 2011 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. + */ + +/* + * Backtracing functions for ARM. + * + * This implementation uses the exception unwinding tables provided by + * the compiler to unwind call frames. Refer to the ARM Exception Handling ABI + * documentation (EHABI) for more details about what's going on here. + * + * An ELF binary may contain an EXIDX section that provides an index to + * the exception handling table of each function, sorted by program + * counter address. + * + * This implementation also supports unwinding other processes via ptrace(). + * In that case, the EXIDX section is found by reading the ELF section table + * structures using ptrace(). + * + * Because the tables are used for exception handling, it can happen that + * a given function will not have an exception handling table. In particular, + * exceptions are assumed to only ever be thrown at call sites. Therefore, + * by definition leaf functions will not have exception handling tables. + * This may make unwinding impossible in some cases although we can still get + * some idea of the call stack by examining the PC and LR registers. + * + * As we are only interested in backtrace information, we do not need + * to perform all of the work of unwinding such as restoring register + * state and running cleanup functions. Unwinding is performed virtually on + * an abstract machine context consisting of just the ARM core registers. + * Furthermore, we do not run generic "personality functions" because + * we may not be in a position to execute arbitrary code, especially if + * we are running in a signal handler or using ptrace()! + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include "../backtrace-arch.h" +#include "../backtrace-helper.h" +#include "../ptrace-arch.h" +#include <corkscrew/ptrace.h> + +#include <stdlib.h> +#include <signal.h> +#include <stdbool.h> +#include <limits.h> +#include <errno.h> +#include <sys/ptrace.h> +#include <sys/exec_elf.h> +#include <cutils/log.h> + +/* Machine context at the time a signal was raised. */ +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + struct sigcontext { + uint32_t trap_no; + uint32_t error_code; + uint32_t oldmask; + uint32_t gregs[16]; + uint32_t arm_cpsr; + uint32_t fault_address; + } uc_mcontext; + uint32_t uc_sigmask; +} ucontext_t; + +/* Unwind state. */ +typedef struct { + uint32_t gregs[16]; +} unwind_state_t; + +static const int R_SP = 13; +static const int R_LR = 14; +static const int R_PC = 15; + +/* Special EXIDX value that indicates that a frame cannot be unwound. */ +static const uint32_t EXIDX_CANTUNWIND = 1; + +/* Get the EXIDX section start and size for the module that contains a + * given program counter address. + * + * When the executable is statically linked, the EXIDX section can be + * accessed by querying the values of the __exidx_start and __exidx_end + * symbols. + * + * When the executable is dynamically linked, the linker exports a function + * called dl_unwind_find_exidx that obtains the EXIDX section for a given + * absolute program counter address. + * + * Bionic exports a helpful function called __gnu_Unwind_Find_exidx that + * handles both cases, so we use that here. + */ +typedef long unsigned int* _Unwind_Ptr; +extern _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr pc, int *pcount); + +static uintptr_t find_exidx(uintptr_t pc, size_t* out_exidx_size) { + int count; + uintptr_t start = (uintptr_t)__gnu_Unwind_Find_exidx((_Unwind_Ptr)pc, &count); + *out_exidx_size = count; + return start; +} + +/* Transforms a 31-bit place-relative offset to an absolute address. + * We assume the most significant bit is clear. */ +static uintptr_t prel_to_absolute(uintptr_t place, uint32_t prel_offset) { + return place + (((int32_t)(prel_offset << 1)) >> 1); +} + +static uintptr_t get_exception_handler(const memory_t* memory, + const map_info_t* map_info_list, uintptr_t pc) { + if (!pc) { + ALOGV("get_exception_handler: pc is zero, no handler"); + return 0; + } + + uintptr_t exidx_start; + size_t exidx_size; + const map_info_t* mi; + if (memory->tid < 0) { + mi = NULL; + exidx_start = find_exidx(pc, &exidx_size); + } else { + mi = find_map_info(map_info_list, pc); + if (mi && mi->data) { + const map_info_data_t* data = (const map_info_data_t*)mi->data; + exidx_start = data->exidx_start; + exidx_size = data->exidx_size; + } else { + exidx_start = 0; + exidx_size = 0; + } + } + + uintptr_t handler = 0; + int32_t handler_index = -1; + if (exidx_start) { + uint32_t low = 0; + uint32_t high = exidx_size; + while (low < high) { + uint32_t index = (low + high) / 2; + uintptr_t entry = exidx_start + index * 8; + uint32_t entry_prel_pc; + ALOGV("XXX low=%u, high=%u, index=%u", low, high, index); + if (!try_get_word(memory, entry, &entry_prel_pc)) { + break; + } + uintptr_t entry_pc = prel_to_absolute(entry, entry_prel_pc); + ALOGV("XXX entry_pc=0x%08x", entry_pc); + if (pc < entry_pc) { + high = index; + continue; + } + if (index + 1 < exidx_size) { + uintptr_t next_entry = entry + 8; + uint32_t next_entry_prel_pc; + if (!try_get_word(memory, next_entry, &next_entry_prel_pc)) { + break; + } + uintptr_t next_entry_pc = prel_to_absolute(next_entry, next_entry_prel_pc); + ALOGV("XXX next_entry_pc=0x%08x", next_entry_pc); + if (pc >= next_entry_pc) { + low = index + 1; + continue; + } + } + + uintptr_t entry_handler_ptr = entry + 4; + uint32_t entry_handler; + if (!try_get_word(memory, entry_handler_ptr, &entry_handler)) { + break; + } + if (entry_handler & (1L << 31)) { + handler = entry_handler_ptr; // in-place handler data + } else if (entry_handler != EXIDX_CANTUNWIND) { + handler = prel_to_absolute(entry_handler_ptr, entry_handler); + } + handler_index = index; + break; + } + } + if (mi) { + ALOGV("get_exception_handler: pc=0x%08x, module='%s', module_start=0x%08x, " + "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d", + pc, mi->name, mi->start, exidx_start, exidx_size, handler, handler_index); + } else { + ALOGV("get_exception_handler: pc=0x%08x, " + "exidx_start=0x%08x, exidx_size=%d, handler=0x%08x, handler_index=%d", + pc, exidx_start, exidx_size, handler, handler_index); + } + return handler; +} + +typedef struct { + uintptr_t ptr; + uint32_t word; +} byte_stream_t; + +static bool try_next_byte(const memory_t* memory, byte_stream_t* stream, uint8_t* out_value) { + uint8_t result; + switch (stream->ptr & 3) { + case 0: + if (!try_get_word(memory, stream->ptr, &stream->word)) { + *out_value = 0; + return false; + } + *out_value = stream->word >> 24; + break; + + case 1: + *out_value = stream->word >> 16; + break; + + case 2: + *out_value = stream->word >> 8; + break; + + default: + *out_value = stream->word; + break; + } + + ALOGV("next_byte: ptr=0x%08x, value=0x%02x", stream->ptr, *out_value); + stream->ptr += 1; + return true; +} + +static void set_reg(unwind_state_t* state, uint32_t reg, uint32_t value) { + ALOGV("set_reg: reg=%d, value=0x%08x", reg, value); + state->gregs[reg] = value; +} + +static bool try_pop_registers(const memory_t* memory, unwind_state_t* state, uint32_t mask) { + uint32_t sp = state->gregs[R_SP]; + bool sp_updated = false; + for (int i = 0; i < 16; i++) { + if (mask & (1 << i)) { + uint32_t value; + if (!try_get_word(memory, sp, &value)) { + return false; + } + if (i == R_SP) { + sp_updated = true; + } + set_reg(state, i, value); + sp += 4; + } + } + if (!sp_updated) { + set_reg(state, R_SP, sp); + } + return true; +} + +/* Executes a built-in personality routine as defined in the EHABI. + * Returns true if unwinding should continue. + * + * The data for the built-in personality routines consists of a sequence + * of unwinding instructions, followed by a sequence of scope descriptors, + * each of which has a length and offset encoded using 16-bit or 32-bit + * values. + * + * We only care about the unwinding instructions. They specify the + * operations of an abstract machine whose purpose is to transform the + * virtual register state (including the stack pointer) such that + * the call frame is unwound and the PC register points to the call site. + */ +static bool execute_personality_routine(const memory_t* memory, + unwind_state_t* state, byte_stream_t* stream, int pr_index) { + size_t size; + switch (pr_index) { + case 0: // Personality routine #0, short frame, descriptors have 16-bit scope. + size = 3; + break; + case 1: // Personality routine #1, long frame, descriptors have 16-bit scope. + case 2: { // Personality routine #2, long frame, descriptors have 32-bit scope. + uint8_t size_byte; + if (!try_next_byte(memory, stream, &size_byte)) { + return false; + } + size = (uint32_t)size_byte * sizeof(uint32_t) + 2; + break; + } + default: // Unknown personality routine. Stop here. + return false; + } + + bool pc_was_set = false; + while (size--) { + uint8_t op; + if (!try_next_byte(memory, stream, &op)) { + return false; + } + if ((op & 0xc0) == 0x00) { + // "vsp = vsp + (xxxxxx << 2) + 4" + set_reg(state, R_SP, state->gregs[R_SP] + ((op & 0x3f) << 2) + 4); + } else if ((op & 0xc0) == 0x40) { + // "vsp = vsp - (xxxxxx << 2) - 4" + set_reg(state, R_SP, state->gregs[R_SP] - ((op & 0x3f) << 2) - 4); + } else if ((op & 0xf0) == 0x80) { + uint8_t op2; + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + uint32_t mask = (((uint32_t)op & 0x0f) << 12) | ((uint32_t)op2 << 4); + if (mask) { + // "Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}" + if (!try_pop_registers(memory, state, mask)) { + return false; + } + if (mask & (1 << R_PC)) { + pc_was_set = true; + } + } else { + // "Refuse to unwind" + return false; + } + } else if ((op & 0xf0) == 0x90) { + if (op != 0x9d && op != 0x9f) { + // "Set vsp = r[nnnn]" + set_reg(state, R_SP, state->gregs[op & 0x0f]); + } else { + // "Reserved as prefix for ARM register to register moves" + // "Reserved as prefix for Intel Wireless MMX register to register moves" + return false; + } + } else if ((op & 0xf8) == 0xa0) { + // "Pop r4-r[4+nnn]" + uint32_t mask = (0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0; + if (!try_pop_registers(memory, state, mask)) { + return false; + } + } else if ((op & 0xf8) == 0xa8) { + // "Pop r4-r[4+nnn], r14" + uint32_t mask = ((0x0ff0 >> (7 - (op & 0x07))) & 0x0ff0) | 0x4000; + if (!try_pop_registers(memory, state, mask)) { + return false; + } + } else if (op == 0xb0) { + // "Finish" + break; + } else if (op == 0xb1) { + uint8_t op2; + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + if (op2 != 0x00 && (op2 & 0xf0) == 0x00) { + // "Pop integer registers under mask {r3, r2, r1, r0}" + if (!try_pop_registers(memory, state, op2)) { + return false; + } + } else { + // "Spare" + return false; + } + } else if (op == 0xb2) { + // "vsp = vsp + 0x204 + (uleb128 << 2)" + uint32_t value = 0; + uint32_t shift = 0; + uint8_t op2; + do { + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + value |= (op2 & 0x7f) << shift; + shift += 7; + } while (op2 & 0x80); + set_reg(state, R_SP, state->gregs[R_SP] + (value << 2) + 0x204); + } else if (op == 0xb3) { + // "Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDX" + uint8_t op2; + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 12); + } else if ((op & 0xf8) == 0xb8) { + // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDX" + set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 12); + } else if ((op & 0xf8) == 0xc0) { + // "Intel Wireless MMX pop wR[10]-wR[10+nnn]" + set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8); + } else if (op == 0xc6) { + // "Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc]" + uint8_t op2; + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); + } else if (op == 0xc7) { + uint8_t op2; + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + if (op2 != 0x00 && (op2 & 0xf0) == 0x00) { + // "Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}" + set_reg(state, R_SP, state->gregs[R_SP] + __builtin_popcount(op2) * 4); + } else { + // "Spare" + return false; + } + } else if (op == 0xc8) { + // "Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] + // saved (as if) by FSTMFD" + uint8_t op2; + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); + } else if (op == 0xc9) { + // "Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) by FSTMFDD" + uint8_t op2; + if (!(size--) || !try_next_byte(memory, stream, &op2)) { + return false; + } + set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op2 & 0x0f) * 8 + 8); + } else if ((op == 0xf8) == 0xd0) { + // "Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by FSTMFDD" + set_reg(state, R_SP, state->gregs[R_SP] + (uint32_t)(op & 0x07) * 8 + 8); + } else { + // "Spare" + return false; + } + } + if (!pc_was_set) { + set_reg(state, R_PC, state->gregs[R_LR]); + } + return true; +} + +static bool try_get_half_word(const memory_t* memory, uint32_t pc, uint16_t* out_value) { + uint32_t word; + if (try_get_word(memory, pc & ~2, &word)) { + *out_value = pc & 2 ? word >> 16 : word & 0xffff; + return true; + } + return false; +} + +uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) { + if (pc & 1) { + /* Thumb mode - need to check whether the bl(x) has long offset or not. + * Examples: + * + * arm blx in the middle of thumb: + * 187ae: 2300 movs r3, #0 + * 187b0: f7fe ee1c blx 173ec + * 187b4: 2c00 cmp r4, #0 + * + * arm bl in the middle of thumb: + * 187d8: 1c20 adds r0, r4, #0 + * 187da: f136 fd15 bl 14f208 + * 187de: 2800 cmp r0, #0 + * + * pure thumb: + * 18894: 189b adds r3, r3, r2 + * 18896: 4798 blx r3 + * 18898: b001 add sp, #4 + */ + uint16_t prev1, prev2; + if (try_get_half_word(memory, pc - 5, &prev1) + && ((prev1 & 0xf000) == 0xf000) + && try_get_half_word(memory, pc - 3, &prev2) + && ((prev2 & 0xe000) == 0xe000)) { + pc -= 4; // long offset + } else { + pc -= 2; + } + } else { + /* ARM mode, all instructions are 32bit. Yay! */ + pc -= 4; + } + return pc; +} + +static ssize_t unwind_backtrace_common(const memory_t* memory, + const map_info_t* map_info_list, + unwind_state_t* state, backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth) { + size_t ignored_frames = 0; + size_t returned_frames = 0; + + for (size_t index = 0; returned_frames < max_depth; index++) { + uintptr_t pc = index ? rewind_pc_arch(memory, state->gregs[R_PC]) + : state->gregs[R_PC]; + backtrace_frame_t* frame = add_backtrace_entry(pc, + backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames); + if (frame) { + frame->stack_top = state->gregs[R_SP]; + } + + uintptr_t handler = get_exception_handler(memory, map_info_list, pc); + if (!handler) { + // If there is no handler for the PC and this is the first frame, + // then the program may have branched to an invalid address. + // Try starting from the LR instead, otherwise stop unwinding. + if (index == 0 && state->gregs[R_LR] + && state->gregs[R_LR] != state->gregs[R_PC]) { + set_reg(state, R_PC, state->gregs[R_LR]); + continue; + } else { + break; + } + } + + byte_stream_t stream; + stream.ptr = handler; + uint8_t pr; + if (!try_next_byte(memory, &stream, &pr)) { + break; + } + if ((pr & 0xf0) != 0x80) { + // The first word is a place-relative pointer to a generic personality + // routine function. We don't support invoking such functions, so stop here. + break; + } + + // The first byte indicates the personality routine to execute. + // Following bytes provide instructions to the personality routine. + if (!execute_personality_routine(memory, state, &stream, pr & 0x0f)) { + break; + } + if (frame && state->gregs[R_SP] > frame->stack_top) { + frame->stack_size = state->gregs[R_SP] - frame->stack_top; + } + if (!state->gregs[R_PC]) { + break; + } + } + + // Ran out of frames that we could unwind using handlers. + // Add a final entry for the LR if it looks sane and call it good. + if (returned_frames < max_depth + && state->gregs[R_LR] + && state->gregs[R_LR] != state->gregs[R_PC] + && is_executable_map(map_info_list, state->gregs[R_LR])) { + // We don't know where the stack for this extra frame starts so we + // don't return any stack information for it. + add_backtrace_entry(rewind_pc_arch(memory, state->gregs[R_LR]), + backtrace, ignore_depth, max_depth, &ignored_frames, &returned_frames); + } + return returned_frames; +} + +ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, + const map_info_t* map_info_list, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { + const ucontext_t* uc = (const ucontext_t*)sigcontext; + + unwind_state_t state; + for (int i = 0; i < 16; i++) { + state.gregs[i] = uc->uc_mcontext.gregs[i]; + } + + memory_t memory; + init_memory(&memory, map_info_list); + return unwind_backtrace_common(&memory, map_info_list, &state, + backtrace, ignore_depth, max_depth); +} + +ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { + struct pt_regs regs; + if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + return -1; + } + + unwind_state_t state; + for (int i = 0; i < 16; i++) { + state.gregs[i] = regs.uregs[i]; + } + + memory_t memory; + init_memory_ptrace(&memory, tid); + return unwind_backtrace_common(&memory, context->map_info_list, &state, + backtrace, ignore_depth, max_depth); +} diff --git a/libcorkscrew/arch-arm/ptrace-arm.c b/libcorkscrew/arch-arm/ptrace-arm.c new file mode 100644 index 0000000..868230c --- /dev/null +++ b/libcorkscrew/arch-arm/ptrace-arm.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include "../ptrace-arch.h" + +#include <sys/exec_elf.h> +#include <cutils/log.h> + +#ifndef PT_ARM_EXIDX +#define PT_ARM_EXIDX 0x70000001 +#endif + +static void load_exidx_header(pid_t pid, map_info_t* mi, + uintptr_t* out_exidx_start, size_t* out_exidx_size) { + uint32_t elf_phoff; + uint32_t elf_phentsize_phnum; + if (try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phoff), &elf_phoff) + && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), + &elf_phentsize_phnum)) { + uint32_t elf_phentsize = elf_phentsize_phnum >> 16; + uint32_t elf_phnum = elf_phentsize_phnum & 0xffff; + for (uint32_t i = 0; i < elf_phnum; i++) { + uintptr_t elf_phdr = mi->start + elf_phoff + i * elf_phentsize; + uint32_t elf_phdr_type; + if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_type), &elf_phdr_type)) { + break; + } + if (elf_phdr_type == PT_ARM_EXIDX) { + uint32_t elf_phdr_offset; + uint32_t elf_phdr_filesz; + if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), + &elf_phdr_offset) + || !try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_filesz), + &elf_phdr_filesz)) { + break; + } + *out_exidx_start = mi->start + elf_phdr_offset; + *out_exidx_size = elf_phdr_filesz / 8; + ALOGV("Parsed EXIDX header info for %s: start=0x%08x, size=%d", mi->name, + *out_exidx_start, *out_exidx_size); + return; + } + } + } + *out_exidx_start = 0; + *out_exidx_size = 0; +} + +void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { + load_exidx_header(pid, mi, &data->exidx_start, &data->exidx_size); +} + +void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) { +} diff --git a/libcorkscrew/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c new file mode 100644 index 0000000..24fadcb --- /dev/null +++ b/libcorkscrew/arch-x86/backtrace-x86.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2011 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. + */ + +/* + * Backtracing functions for x86. + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include "../backtrace-arch.h" +#include "../backtrace-helper.h" +#include <corkscrew/ptrace.h> + +#include <stdlib.h> +#include <signal.h> +#include <stdbool.h> +#include <limits.h> +#include <errno.h> +#include <sys/ptrace.h> +#include <sys/exec_elf.h> +#include <cutils/log.h> + +/* Machine context at the time a signal was raised. */ +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + struct sigcontext { + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint32_t cs; + uint32_t efl; + uint32_t uesp; + uint32_t ss; + void* fpregs; + uint32_t oldmask; + uint32_t cr2; + } uc_mcontext; + uint32_t uc_sigmask; +} ucontext_t; + +/* Unwind state. */ +typedef struct { + uint32_t ebp; + uint32_t eip; + uint32_t esp; +} unwind_state_t; + +uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) { + // TODO: Implement for x86. + return pc; +} + +static ssize_t unwind_backtrace_common(const memory_t* memory, + const map_info_t* map_info_list, + unwind_state_t* state, backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth) { + size_t ignored_frames = 0; + size_t returned_frames = 0; + + for (size_t index = 0; state->ebp && returned_frames < max_depth; index++) { + backtrace_frame_t* frame = add_backtrace_entry( + index ? rewind_pc_arch(memory, state->eip) : state->eip, + backtrace, ignore_depth, max_depth, + &ignored_frames, &returned_frames); + uint32_t next_esp = state->ebp + 8; + if (frame) { + frame->stack_top = state->esp; + if (state->esp < next_esp) { + frame->stack_size = next_esp - state->esp; + } + } + state->esp = next_esp; + if (!try_get_word(memory, state->ebp + 4, &state->eip) + || !try_get_word(memory, state->ebp, &state->ebp) + || !state->eip) { + break; + } + } + + return returned_frames; +} + +ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, + const map_info_t* map_info_list, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { + const ucontext_t* uc = (const ucontext_t*)sigcontext; + + unwind_state_t state; + state.ebp = uc->uc_mcontext.ebp; + state.eip = uc->uc_mcontext.eip; + state.esp = uc->uc_mcontext.esp; + + memory_t memory; + init_memory(&memory, map_info_list); + return unwind_backtrace_common(&memory, map_info_list, + &state, backtrace, ignore_depth, max_depth); +} + +ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { + pt_regs_x86_t regs; + if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + return -1; + } + + unwind_state_t state; + state.ebp = regs.ebp; + state.eip = regs.eip; + state.esp = regs.esp; + + memory_t memory; + init_memory_ptrace(&memory, tid); + return unwind_backtrace_common(&memory, context->map_info_list, + &state, backtrace, ignore_depth, max_depth); +} diff --git a/nexus/SupplicantEvent.cpp b/libcorkscrew/arch-x86/ptrace-x86.c index faf7b45..f0ea110 100644 --- a/nexus/SupplicantEvent.cpp +++ b/libcorkscrew/arch-x86/ptrace-x86.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 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. @@ -14,16 +14,15 @@ * limitations under the License. */ -#include <stdlib.h> +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 -#define LOG_TAG "SupplicantEvent" -#include <cutils/log.h> +#include "../ptrace-arch.h" -#include "SupplicantEvent.h" +#include <cutils/log.h> -#include "libwpa_client/wpa_ctrl.h" +void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { +} -SupplicantEvent::SupplicantEvent(int type, int level) { - mType = type; - mLevel = level; +void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data) { } diff --git a/libcorkscrew/backtrace-arch.h b/libcorkscrew/backtrace-arch.h new file mode 100644 index 0000000..a46f80b --- /dev/null +++ b/libcorkscrew/backtrace-arch.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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. + */ + +/* Architecture dependent functions. */ + +#ifndef _CORKSCREW_BACKTRACE_ARCH_H +#define _CORKSCREW_BACKTRACE_ARCH_H + +#include "ptrace-arch.h" +#include <corkscrew/backtrace.h> + +#include <signal.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Rewind the program counter by one instruction. */ +uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc); + +ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo, void* sigcontext, + const map_info_t* map_info_list, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); + +ssize_t unwind_backtrace_ptrace_arch(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_BACKTRACE_ARCH_H diff --git a/libcorkscrew/backtrace-helper.c b/libcorkscrew/backtrace-helper.c new file mode 100644 index 0000000..bf9d3f3 --- /dev/null +++ b/libcorkscrew/backtrace-helper.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include "backtrace-helper.h" + +#include <cutils/log.h> + +backtrace_frame_t* add_backtrace_entry(uintptr_t pc, backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth, + size_t* ignored_frames, size_t* returned_frames) { + if (*ignored_frames < ignore_depth) { + *ignored_frames += 1; + return NULL; + } + if (*returned_frames >= max_depth) { + return NULL; + } + backtrace_frame_t* frame = &backtrace[*returned_frames]; + frame->absolute_pc = pc; + frame->stack_top = 0; + frame->stack_size = 0; + *returned_frames += 1; + return frame; +} diff --git a/libcorkscrew/backtrace-helper.h b/libcorkscrew/backtrace-helper.h new file mode 100644 index 0000000..4d8a874 --- /dev/null +++ b/libcorkscrew/backtrace-helper.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 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. + */ + +/* Backtrace helper functions. */ + +#ifndef _CORKSCREW_BACKTRACE_HELPER_H +#define _CORKSCREW_BACKTRACE_HELPER_H + +#include <corkscrew/backtrace.h> +#include <sys/types.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Add a program counter to a backtrace if it will fit. + * Returns the newly added frame, or NULL if none. + */ +backtrace_frame_t* add_backtrace_entry(uintptr_t pc, + backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth, + size_t* ignored_frames, size_t* returned_frames); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_BACKTRACE_HELPER_H diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c new file mode 100644 index 0000000..fa21574 --- /dev/null +++ b/libcorkscrew/backtrace.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include "backtrace-arch.h" +#include "backtrace-helper.h" +#include "ptrace-arch.h" +#include <corkscrew/map_info.h> +#include <corkscrew/symbol_table.h> +#include <corkscrew/ptrace.h> +#include <corkscrew/demangle.h> + +#include <unistd.h> +#include <signal.h> +#include <pthread.h> +#include <unwind.h> +#include <sys/exec_elf.h> +#include <cutils/log.h> +#include <cutils/atomic.h> + +#if HAVE_DLADDR +#include <dlfcn.h> +#endif + +typedef struct { + backtrace_frame_t* backtrace; + size_t ignore_depth; + size_t max_depth; + size_t ignored_frames; + size_t returned_frames; + memory_t memory; +} backtrace_state_t; + +static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) { + backtrace_state_t* state = (backtrace_state_t*)arg; + uintptr_t pc = _Unwind_GetIP(context); + if (pc) { + // TODO: Get information about the stack layout from the _Unwind_Context. + // This will require a new architecture-specific function to query + // the appropriate registers. Current callers of unwind_backtrace + // don't need this information, so we won't bother collecting it just yet. + add_backtrace_entry(rewind_pc_arch(&state->memory, pc), state->backtrace, + state->ignore_depth, state->max_depth, + &state->ignored_frames, &state->returned_frames); + } + return state->returned_frames < state->max_depth ? _URC_NO_REASON : _URC_END_OF_STACK; +} + +ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { + ALOGV("Unwinding current thread %d.", gettid()); + + map_info_t* milist = acquire_my_map_info_list(); + + backtrace_state_t state; + state.backtrace = backtrace; + state.ignore_depth = ignore_depth; + state.max_depth = max_depth; + state.ignored_frames = 0; + state.returned_frames = 0; + init_memory(&state.memory, milist); + + _Unwind_Reason_Code rc =_Unwind_Backtrace(unwind_backtrace_callback, &state); + + release_my_map_info_list(milist); + + if (state.returned_frames) { + return state.returned_frames; + } + return rc == _URC_END_OF_STACK ? 0 : -1; +} + +#ifdef CORKSCREW_HAVE_ARCH +static const int32_t STATE_DUMPING = -1; +static const int32_t STATE_DONE = -2; +static const int32_t STATE_CANCEL = -3; + +static pthread_mutex_t g_unwind_signal_mutex = PTHREAD_MUTEX_INITIALIZER; +static volatile struct { + int32_t tid_state; + const map_info_t* map_info_list; + backtrace_frame_t* backtrace; + size_t ignore_depth; + size_t max_depth; + size_t returned_frames; +} g_unwind_signal_state; + +static void unwind_backtrace_thread_signal_handler(int n, siginfo_t* siginfo, void* sigcontext) { + if (!android_atomic_acquire_cas(gettid(), STATE_DUMPING, &g_unwind_signal_state.tid_state)) { + g_unwind_signal_state.returned_frames = unwind_backtrace_signal_arch( + siginfo, sigcontext, + g_unwind_signal_state.map_info_list, + g_unwind_signal_state.backtrace, + g_unwind_signal_state.ignore_depth, + g_unwind_signal_state.max_depth); + android_atomic_release_store(STATE_DONE, &g_unwind_signal_state.tid_state); + } else { + ALOGV("Received spurious SIGURG on thread %d that was intended for thread %d.", + gettid(), android_atomic_acquire_load(&g_unwind_signal_state.tid_state)); + } +} +#endif + +extern int tgkill(int tgid, int tid, int sig); + +ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth) { + if (tid == gettid()) { + return unwind_backtrace(backtrace, ignore_depth + 1, max_depth); + } + + ALOGV("Unwinding thread %d from thread %d.", tid, gettid()); + +#ifdef CORKSCREW_HAVE_ARCH + struct sigaction act; + struct sigaction oact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = unwind_backtrace_thread_signal_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; + sigemptyset(&act.sa_mask); + + pthread_mutex_lock(&g_unwind_signal_mutex); + map_info_t* milist = acquire_my_map_info_list(); + + ssize_t frames = -1; + if (!sigaction(SIGURG, &act, &oact)) { + g_unwind_signal_state.map_info_list = milist; + g_unwind_signal_state.backtrace = backtrace; + g_unwind_signal_state.ignore_depth = ignore_depth; + g_unwind_signal_state.max_depth = max_depth; + g_unwind_signal_state.returned_frames = 0; + android_atomic_release_store(tid, &g_unwind_signal_state.tid_state); + + // Signal the specific thread that we want to dump. + int32_t tid_state = tid; + if (tgkill(getpid(), tid, SIGURG)) { + ALOGV("Failed to send SIGURG to thread %d.", tid); + } else { + // Wait for the other thread to start dumping the stack, or time out. + int wait_millis = 250; + for (;;) { + tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); + if (tid_state != tid) { + break; + } + if (wait_millis--) { + ALOGV("Waiting for thread %d to start dumping the stack...", tid); + usleep(1000); + } else { + ALOGV("Timed out waiting for thread %d to start dumping the stack.", tid); + break; + } + } + } + + // Try to cancel the dump if it has not started yet. + if (tid_state == tid) { + if (!android_atomic_acquire_cas(tid, STATE_CANCEL, &g_unwind_signal_state.tid_state)) { + ALOGV("Canceled thread %d stack dump.", tid); + tid_state = STATE_CANCEL; + } else { + tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); + } + } + + // Wait indefinitely for the dump to finish or be canceled. + // We cannot apply a timeout here because the other thread is accessing state that + // is owned by this thread, such as milist. It should not take very + // long to take the dump once started. + while (tid_state == STATE_DUMPING) { + ALOGV("Waiting for thread %d to finish dumping the stack...", tid); + usleep(1000); + tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); + } + + if (tid_state == STATE_DONE) { + frames = g_unwind_signal_state.returned_frames; + } + + sigaction(SIGURG, &oact, NULL); + } + + release_my_map_info_list(milist); + pthread_mutex_unlock(&g_unwind_signal_mutex); + return frames; +#else + return -1; +#endif +} + +ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { +#ifdef CORKSCREW_HAVE_ARCH + return unwind_backtrace_ptrace_arch(tid, context, backtrace, ignore_depth, max_depth); +#else + return -1; +#endif +} + +static void init_backtrace_symbol(backtrace_symbol_t* symbol, uintptr_t pc) { + symbol->relative_pc = pc; + symbol->relative_symbol_addr = 0; + symbol->map_name = NULL; + symbol->symbol_name = NULL; + symbol->demangled_name = NULL; +} + +void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols) { + map_info_t* milist = acquire_my_map_info_list(); + for (size_t i = 0; i < frames; i++) { + const backtrace_frame_t* frame = &backtrace[i]; + backtrace_symbol_t* symbol = &backtrace_symbols[i]; + init_backtrace_symbol(symbol, frame->absolute_pc); + + const map_info_t* mi = find_map_info(milist, frame->absolute_pc); + if (mi) { + symbol->relative_pc = frame->absolute_pc - mi->start; + if (mi->name[0]) { + symbol->map_name = strdup(mi->name); + } +#if HAVE_DLADDR + Dl_info info; + if (dladdr((const void*)frame->absolute_pc, &info) && info.dli_sname) { + symbol->relative_symbol_addr = (uintptr_t)info.dli_saddr + - (uintptr_t)info.dli_fbase; + symbol->symbol_name = strdup(info.dli_sname); + symbol->demangled_name = demangle_symbol_name(symbol->symbol_name); + } +#endif + } + } + release_my_map_info_list(milist); +} + +void get_backtrace_symbols_ptrace(const ptrace_context_t* context, + const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols) { + for (size_t i = 0; i < frames; i++) { + const backtrace_frame_t* frame = &backtrace[i]; + backtrace_symbol_t* symbol = &backtrace_symbols[i]; + init_backtrace_symbol(symbol, frame->absolute_pc); + + const map_info_t* mi; + const symbol_t* s; + find_symbol_ptrace(context, frame->absolute_pc, &mi, &s); + if (mi) { + symbol->relative_pc = frame->absolute_pc - mi->start; + if (mi->name[0]) { + symbol->map_name = strdup(mi->name); + } + } + if (s) { + symbol->relative_symbol_addr = s->start; + symbol->symbol_name = strdup(s->name); + symbol->demangled_name = demangle_symbol_name(symbol->symbol_name); + } + } +} + +void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames) { + for (size_t i = 0; i < frames; i++) { + backtrace_symbol_t* symbol = &backtrace_symbols[i]; + free(symbol->map_name); + free(symbol->symbol_name); + free(symbol->demangled_name); + init_backtrace_symbol(symbol, 0); + } +} + +void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame, + const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize) { + const char* mapName = symbol->map_name ? symbol->map_name : "<unknown>"; + const char* symbolName = symbol->demangled_name ? symbol->demangled_name : symbol->symbol_name; + size_t fieldWidth = (bufferSize - 80) / 2; + if (symbolName) { + uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; + if (pc_offset) { + snprintf(buffer, bufferSize, "#%02d pc %08x %.*s (%.*s+%u)", + frameNumber, symbol->relative_pc, fieldWidth, mapName, + fieldWidth, symbolName, pc_offset); + } else { + snprintf(buffer, bufferSize, "#%02d pc %08x %.*s (%.*s)", + frameNumber, symbol->relative_pc, fieldWidth, mapName, + fieldWidth, symbolName); + } + } else { + snprintf(buffer, bufferSize, "#%02d pc %08x %.*s", + frameNumber, symbol->relative_pc, fieldWidth, mapName); + } +} diff --git a/nexus/DhcpEvent.h b/libcorkscrew/demangle.c index f77834d..54247cb 100644 --- a/nexus/DhcpEvent.h +++ b/libcorkscrew/demangle.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2011 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. @@ -14,20 +14,17 @@ * limitations under the License. */ -#ifndef _DHCP_EVENT_H -#define _DHCP_EVENT_H +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 -class DhcpEvent { -public: - static const int UNKNOWN = 0; - static const int STOP = 1; - static const int RENEW = 2; - static const int RELEASE = 3; - static const int TIMEOUT = 4; +#include <corkscrew/demangle.h> - static char *toString(int val, char *buffer, int max); +#include <cutils/log.h> - static int parseString(const char *buffer); -}; +extern char *__cxa_demangle (const char *mangled, char *buf, size_t *len, + int *status); -#endif +char* demangle_symbol_name(const char* name) { + // __cxa_demangle handles NULL by returning NULL + return __cxa_demangle(name, 0, 0, 0); +} diff --git a/libcorkscrew/map_info.c b/libcorkscrew/map_info.c new file mode 100644 index 0000000..f33378f --- /dev/null +++ b/libcorkscrew/map_info.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include <corkscrew/map_info.h> + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <pthread.h> +#include <unistd.h> +#include <cutils/log.h> +#include <sys/time.h> + +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static map_info_t* parse_maps_line(const char* line) +{ + unsigned long int start; + unsigned long int end; + char permissions[5]; + int name_pos; + if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end, + permissions, &name_pos) != 3) { + return NULL; + } + + while (isspace(line[name_pos])) { + name_pos += 1; + } + const char* name = line + name_pos; + size_t name_len = strlen(name); + if (name_len && name[name_len - 1] == '\n') { + name_len -= 1; + } + + map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len + 1); + if (mi) { + mi->start = start; + mi->end = end; + mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r'; + mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x'; + mi->data = NULL; + memcpy(mi->name, name, name_len); + mi->name[name_len] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_executable=%d, name=%s", + mi->start, mi->end, mi->is_readable, mi->is_executable, mi->name); + } + return mi; +} + +map_info_t* load_map_info_list(pid_t tid) { + char path[PATH_MAX]; + char line[1024]; + FILE* fp; + map_info_t* milist = NULL; + + snprintf(path, PATH_MAX, "/proc/%d/maps", tid); + fp = fopen(path, "r"); + if (fp) { + while(fgets(line, sizeof(line), fp)) { + map_info_t* mi = parse_maps_line(line); + if (mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + return milist; +} + +void free_map_info_list(map_info_t* milist) { + while (milist) { + map_info_t* next = milist->next; + free(milist); + milist = next; + } +} + +const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr) { + const map_info_t* mi = milist; + while (mi && !(addr >= mi->start && addr < mi->end)) { + mi = mi->next; + } + return mi; +} + +bool is_readable_map(const map_info_t* milist, uintptr_t addr) { + const map_info_t* mi = find_map_info(milist, addr); + return mi && mi->is_readable; +} + +bool is_executable_map(const map_info_t* milist, uintptr_t addr) { + const map_info_t* mi = find_map_info(milist, addr); + return mi && mi->is_executable; +} + +static pthread_mutex_t g_my_map_info_list_mutex = PTHREAD_MUTEX_INITIALIZER; +static map_info_t* g_my_map_info_list = NULL; + +static const int64_t MAX_CACHE_AGE = 5 * 1000 * 1000000LL; + +typedef struct { + uint32_t refs; + int64_t timestamp; +} my_map_info_data_t; + +static int64_t now() { + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &t); + return t.tv_sec * 1000000000LL + t.tv_nsec; +} + +static void dec_ref(map_info_t* milist, my_map_info_data_t* data) { + if (!--data->refs) { + ALOGV("Freed my_map_info_list %p.", milist); + free(data); + free_map_info_list(milist); + } +} + +map_info_t* acquire_my_map_info_list() { + pthread_mutex_lock(&g_my_map_info_list_mutex); + + int64_t time = now(); + if (g_my_map_info_list) { + my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; + int64_t age = time - data->timestamp; + if (age >= MAX_CACHE_AGE) { + ALOGV("Invalidated my_map_info_list %p, age=%lld.", g_my_map_info_list, age); + dec_ref(g_my_map_info_list, data); + g_my_map_info_list = NULL; + } else { + ALOGV("Reusing my_map_info_list %p, age=%lld.", g_my_map_info_list, age); + } + } + + if (!g_my_map_info_list) { + my_map_info_data_t* data = (my_map_info_data_t*)malloc(sizeof(my_map_info_data_t)); + g_my_map_info_list = load_map_info_list(getpid()); + if (g_my_map_info_list) { + ALOGV("Loaded my_map_info_list %p.", g_my_map_info_list); + g_my_map_info_list->data = data; + data->refs = 1; + data->timestamp = time; + } else { + free(data); + } + } + + map_info_t* milist = g_my_map_info_list; + if (milist) { + my_map_info_data_t* data = (my_map_info_data_t*)g_my_map_info_list->data; + data->refs += 1; + } + + pthread_mutex_unlock(&g_my_map_info_list_mutex); + return milist; +} + +void release_my_map_info_list(map_info_t* milist) { + if (milist) { + pthread_mutex_lock(&g_my_map_info_list_mutex); + + my_map_info_data_t* data = (my_map_info_data_t*)milist->data; + dec_ref(milist, data); + + pthread_mutex_unlock(&g_my_map_info_list_mutex); + } +} diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h new file mode 100644 index 0000000..c02df52 --- /dev/null +++ b/libcorkscrew/ptrace-arch.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 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. + */ + +/* Architecture dependent functions. */ + +#ifndef _CORKSCREW_PTRACE_ARCH_H +#define _CORKSCREW_PTRACE_ARCH_H + +#include <corkscrew/ptrace.h> +#include <corkscrew/map_info.h> +#include <corkscrew/symbol_table.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Custom extra data we stuff into map_info_t structures as part + * of our ptrace_context_t. */ +typedef struct { +#ifdef __arm__ + uintptr_t exidx_start; + size_t exidx_size; +#endif + symbol_table_t* symbol_table; +} map_info_data_t; + +void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data); +void free_ptrace_map_info_data_arch(map_info_t* mi, map_info_data_t* data); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_PTRACE_ARCH_H diff --git a/libcorkscrew/ptrace.c b/libcorkscrew/ptrace.c new file mode 100644 index 0000000..cbea8ca --- /dev/null +++ b/libcorkscrew/ptrace.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include "ptrace-arch.h" +#include <corkscrew/ptrace.h> + +#include <errno.h> +#include <sys/ptrace.h> +#include <cutils/log.h> + +static const uint32_t ELF_MAGIC = 0x464C457f; // "ELF\0177" + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#ifndef PAGE_MASK +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#endif + +void init_memory(memory_t* memory, const map_info_t* map_info_list) { + memory->tid = -1; + memory->map_info_list = map_info_list; +} + +void init_memory_ptrace(memory_t* memory, pid_t tid) { + memory->tid = tid; + memory->map_info_list = NULL; +} + +bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value) { + ALOGV("try_get_word: reading word at 0x%08x", ptr); + if (ptr & 3) { + ALOGV("try_get_word: invalid pointer 0x%08x", ptr); + *out_value = 0xffffffffL; + return false; + } + if (memory->tid < 0) { + if (!is_readable_map(memory->map_info_list, ptr)) { + ALOGV("try_get_word: pointer 0x%08x not in a readable map", ptr); + *out_value = 0xffffffffL; + return false; + } + *out_value = *(uint32_t*)ptr; + return true; + } else { + // ptrace() returns -1 and sets errno when the operation fails. + // To disambiguate -1 from a valid result, we clear errno beforehand. + errno = 0; + *out_value = ptrace(PTRACE_PEEKTEXT, memory->tid, (void*)ptr, NULL); + if (*out_value == 0xffffffffL && errno) { + ALOGV("try_get_word: invalid pointer 0x%08x reading from tid %d, " + "ptrace() errno=%d", ptr, memory->tid, errno); + return false; + } + return true; + } +} + +bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value) { + memory_t memory; + init_memory_ptrace(&memory, tid); + return try_get_word(&memory, ptr, out_value); +} + +static void load_ptrace_map_info_data(pid_t pid, map_info_t* mi) { + if (mi->is_executable && mi->is_readable) { + uint32_t elf_magic; + if (try_get_word_ptrace(pid, mi->start, &elf_magic) && elf_magic == ELF_MAGIC) { + map_info_data_t* data = (map_info_data_t*)calloc(1, sizeof(map_info_data_t)); + if (data) { + mi->data = data; + if (mi->name[0]) { + data->symbol_table = load_symbol_table(mi->name); + } +#ifdef CORKSCREW_HAVE_ARCH + load_ptrace_map_info_data_arch(pid, mi, data); +#endif + } + } + } +} + +ptrace_context_t* load_ptrace_context(pid_t pid) { + ptrace_context_t* context = + (ptrace_context_t*)calloc(1, sizeof(ptrace_context_t)); + if (context) { + context->map_info_list = load_map_info_list(pid); + for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) { + load_ptrace_map_info_data(pid, mi); + } + } + return context; +} + +static void free_ptrace_map_info_data(map_info_t* mi) { + map_info_data_t* data = (map_info_data_t*)mi->data; + if (data) { + if (data->symbol_table) { + free_symbol_table(data->symbol_table); + } +#ifdef CORKSCREW_HAVE_ARCH + free_ptrace_map_info_data_arch(mi, data); +#endif + free(data); + mi->data = NULL; + } +} + +void free_ptrace_context(ptrace_context_t* context) { + for (map_info_t* mi = context->map_info_list; mi; mi = mi->next) { + free_ptrace_map_info_data(mi); + } + free_map_info_list(context->map_info_list); +} + +void find_symbol_ptrace(const ptrace_context_t* context, + uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol) { + const map_info_t* mi = find_map_info(context->map_info_list, addr); + const symbol_t* symbol = NULL; + if (mi) { + const map_info_data_t* data = (const map_info_data_t*)mi->data; + if (data && data->symbol_table) { + symbol = find_symbol(data->symbol_table, addr - mi->start); + } + } + *out_map_info = mi; + *out_symbol = symbol; +} diff --git a/libcorkscrew/symbol_table.c b/libcorkscrew/symbol_table.c new file mode 100644 index 0000000..1b97180 --- /dev/null +++ b/libcorkscrew/symbol_table.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2011 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. + */ + +#define LOG_TAG "Corkscrew" +//#define LOG_NDEBUG 0 + +#include <corkscrew/symbol_table.h> + +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/exec_elf.h> +#include <cutils/log.h> + +// Compare function for qsort +static int qcompar(const void *a, const void *b) { + const symbol_t* asym = (const symbol_t*)a; + const symbol_t* bsym = (const symbol_t*)b; + if (asym->start > bsym->start) return 1; + if (asym->start < bsym->start) return -1; + return 0; +} + +// Compare function for bsearch +static int bcompar(const void *key, const void *element) { + uintptr_t addr = *(const uintptr_t*)key; + const symbol_t* symbol = (const symbol_t*)element; + if (addr < symbol->start) return -1; + if (addr >= symbol->end) return 1; + return 0; +} + +symbol_table_t* load_symbol_table(const char *filename) { + symbol_table_t* table = NULL; + ALOGV("Loading symbol table from '%s'.", filename); + + int fd = open(filename, O_RDONLY); + if (fd < 0) { + goto out; + } + + struct stat sb; + if (fstat(fd, &sb)) { + goto out_close; + } + + size_t length = sb.st_size; + char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); + if (base == MAP_FAILED) { + goto out_close; + } + + // Parse the file header + Elf32_Ehdr *hdr = (Elf32_Ehdr*)base; + if (!IS_ELF(*hdr)) { + goto out_close; + } + Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff); + + // Search for the dynamic symbols section + int sym_idx = -1; + int dynsym_idx = -1; + for (Elf32_Half i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB) { + sym_idx = i; + } + if (shdr[i].sh_type == SHT_DYNSYM) { + dynsym_idx = i; + } + } + if (dynsym_idx == -1 && sym_idx == -1) { + goto out_unmap; + } + + table = malloc(sizeof(symbol_table_t)); + if(!table) { + goto out_unmap; + } + table->num_symbols = 0; + + Elf32_Sym *dynsyms = NULL; + int dynnumsyms = 0; + char *dynstr = NULL; + if (dynsym_idx != -1) { + dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset); + dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize; + int dynstr_idx = shdr[dynsym_idx].sh_link; + dynstr = base + shdr[dynstr_idx].sh_offset; + } + + Elf32_Sym *syms = NULL; + int numsyms = 0; + char *str = NULL; + if (sym_idx != -1) { + syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset); + numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize; + int str_idx = shdr[sym_idx].sh_link; + str = base + shdr[str_idx].sh_offset; + } + + int dynsymbol_count = 0; + if (dynsym_idx != -1) { + // Iterate through the dynamic symbol table, and count how many symbols + // are actually defined + for (int i = 0; i < dynnumsyms; i++) { + if (dynsyms[i].st_shndx != SHN_UNDEF) { + dynsymbol_count++; + } + } + } + + size_t symbol_count = 0; + if (sym_idx != -1) { + // Iterate through the symbol table, and count how many symbols + // are actually defined + for (int i = 0; i < numsyms; i++) { + if (syms[i].st_shndx != SHN_UNDEF + && str[syms[i].st_name] + && syms[i].st_value + && syms[i].st_size) { + symbol_count++; + } + } + } + + // Now, create an entry in our symbol table structure for each symbol... + table->num_symbols += symbol_count + dynsymbol_count; + table->symbols = malloc(table->num_symbols * sizeof(symbol_t)); + if (!table->symbols) { + free(table); + table = NULL; + goto out_unmap; + } + + size_t symbol_index = 0; + if (dynsym_idx != -1) { + // ...and populate them + for (int i = 0; i < dynnumsyms; i++) { + if (dynsyms[i].st_shndx != SHN_UNDEF) { + table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name); + table->symbols[symbol_index].start = dynsyms[i].st_value; + table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size; + ALOGV(" [%d] '%s' 0x%08x-0x%08x (DYNAMIC)", + symbol_index, table->symbols[symbol_index].name, + table->symbols[symbol_index].start, table->symbols[symbol_index].end); + symbol_index += 1; + } + } + } + + if (sym_idx != -1) { + // ...and populate them + for (int i = 0; i < numsyms; i++) { + if (syms[i].st_shndx != SHN_UNDEF + && str[syms[i].st_name] + && syms[i].st_value + && syms[i].st_size) { + table->symbols[symbol_index].name = strdup(str + syms[i].st_name); + table->symbols[symbol_index].start = syms[i].st_value; + table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size; + ALOGV(" [%d] '%s' 0x%08x-0x%08x", + symbol_index, table->symbols[symbol_index].name, + table->symbols[symbol_index].start, table->symbols[symbol_index].end); + symbol_index += 1; + } + } + } + + // Sort the symbol table entries, so they can be bsearched later + qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar); + +out_unmap: + munmap(base, length); + +out_close: + close(fd); + +out: + return table; +} + +void free_symbol_table(symbol_table_t* table) { + if (table) { + for (size_t i = 0; i < table->num_symbols; i++) { + free(table->symbols[i].name); + } + free(table->symbols); + free(table); + } +} + +const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) { + if (!table) return NULL; + return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols, + sizeof(symbol_t), bcompar); +} diff --git a/libcutils/Android.mk b/libcutils/Android.mk index b83ce68..5c227b6 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -46,6 +46,7 @@ commonSources := \ record_stream.c \ process_name.c \ properties.c \ + qsort_r_compat.c \ threads.c \ sched_policy.c \ iosched_policy.c \ @@ -107,9 +108,23 @@ include $(BUILD_HOST_STATIC_LIBRARY) # Shared and static library for target # ======================================================== + +# This is needed in LOCAL_C_INCLUDES to access the C library's private +# header named <bionic_time.h> +# +libcutils_c_includes := bionic/libc/private + include $(CLEAR_VARS) LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) ashmem-dev.c mq.c android_reboot.c partition_utils.c uevent.c qtaguid.c klog.c +LOCAL_SRC_FILES := $(commonSources) \ + android_reboot.c \ + ashmem-dev.c \ + debugger.c \ + klog.c \ + mq.c \ + partition_utils.c \ + qtaguid.c \ + uevent.c ifeq ($(TARGET_ARCH),arm) LOCAL_SRC_FILES += arch-arm/memset32.S @@ -126,7 +141,7 @@ endif # !x86-atom endif # !sh endif # !arm -LOCAL_C_INCLUDES := $(KERNEL_HEADERS) +LOCAL_C_INCLUDES := $(libcutils_c_includes) $(KERNEL_HEADERS) LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(targetSmpFlag) include $(BUILD_STATIC_LIBRARY) @@ -136,6 +151,7 @@ LOCAL_MODULE := libcutils LOCAL_WHOLE_STATIC_LIBRARIES := libcutils LOCAL_SHARED_LIBRARIES := liblog LOCAL_CFLAGS += $(targetSmpFlag) +LOCAL_C_INCLUDES := $(libcutils_c_includes) include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) diff --git a/libcutils/debugger.c b/libcutils/debugger.c new file mode 100644 index 0000000..9425006 --- /dev/null +++ b/libcutils/debugger.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <unistd.h> + +#include <cutils/debugger.h> +#include <cutils/sockets.h> + +int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) { + int s = socket_local_client(DEBUGGER_SOCKET_NAME, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if (s < 0) { + return -1; + } + + debugger_msg_t msg; + msg.tid = tid; + msg.action = DEBUGGER_ACTION_DUMP_TOMBSTONE; + + int result = 0; + if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { + result = -1; + } else { + char ack; + if (TEMP_FAILURE_RETRY(read(s, &ack, 1)) != 1) { + result = -1; + } else { + if (pathbuf && pathlen) { + ssize_t n = TEMP_FAILURE_RETRY(read(s, pathbuf, pathlen - 1)); + if (n <= 0) { + result = -1; + } else { + pathbuf[n] = '\0'; + } + } + } + } + TEMP_FAILURE_RETRY(close(s)); + return result; +} + +int dump_backtrace_to_file(pid_t tid, int fd) { + int s = socket_local_client(DEBUGGER_SOCKET_NAME, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if (s < 0) { + return -1; + } + + debugger_msg_t msg; + msg.tid = tid; + msg.action = DEBUGGER_ACTION_DUMP_BACKTRACE; + + int result = 0; + if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { + result = -1; + } else { + char ack; + if (TEMP_FAILURE_RETRY(read(s, &ack, 1)) != 1) { + result = -1; + } else { + char buffer[4096]; + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)))) > 0) { + if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) { + result = -1; + break; + } + } + } + } + TEMP_FAILURE_RETRY(close(s)); + return result; +} diff --git a/libcutils/process_name.c b/libcutils/process_name.c index b235429..bda9d08 100644 --- a/libcutils/process_name.c +++ b/libcutils/process_name.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <stdlib.h> #include <string.h> #include <cutils/process_name.h> #include <cutils/properties.h> diff --git a/libcutils/qsort_r_compat.c b/libcutils/qsort_r_compat.c new file mode 100644 index 0000000..8971cb5 --- /dev/null +++ b/libcutils/qsort_r_compat.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <cutils/qsort_r_compat.h> + +#if HAVE_BSD_QSORT_R + +/* + * BSD qsort_r parameter order is as we have defined here. + */ + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void*)) { + qsort_r(base, nel, width, thunk, compar); +} + +#elif HAVE_GNU_QSORT_R + +/* + * GNU qsort_r parameter order places the thunk parameter last. + */ + +struct compar_data { + void* thunk; + int (*compar)(void*, const void* , const void*); +}; + +static int compar_wrapper(const void* a, const void* b, void* data) { + struct compar_data* compar_data = (struct compar_data*)data; + return compar_data->compar(compar_data->thunk, a, b); +} + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void*)) { + struct compar_data compar_data; + compar_data.thunk = thunk; + compar_data.compar = compar; + qsort_r(base, nel, width, compar_wrapper, &compar_data); +} + +#else + +/* + * Emulate qsort_r using thread local storage to access the thunk data. + */ + +#include <cutils/threads.h> + +static thread_store_t compar_data_key = THREAD_STORE_INITIALIZER; + +struct compar_data { + void* thunk; + int (*compar)(void*, const void* , const void*); +}; + +static int compar_wrapper(const void* a, const void* b) { + struct compar_data* compar_data = (struct compar_data*)thread_store_get(&compar_data_key); + return compar_data->compar(compar_data->thunk, a, b); +} + +void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, + int (*compar)(void*, const void* , const void*)) { + struct compar_data compar_data; + compar_data.thunk = thunk; + compar_data.compar = compar; + thread_store_set(&compar_data_key, &compar_data, NULL); + qsort(base, nel, width, compar_wrapper); +} + +#endif diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c index 1c57774..5bb8176 100644 --- a/libcutils/qtaguid.c +++ b/libcutils/qtaguid.c @@ -99,9 +99,7 @@ static int write_param(const char *param_path, const char *value) { int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) { char lineBuf[CTRL_MAX_INPUT_LEN]; int res; - /* Doing java-land a favor, enforcing "long" */ - uint64_t kTag = ((uint64_t)tag << 32) & ~(1LLU<<63); - + uint64_t kTag = ((uint64_t)tag << 32); pthread_once(&resTrackInitDone, qtaguid_resTrack); diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index f9c111e..d20d217 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -16,24 +16,31 @@ ** limitations under the License. */ +#define LOG_TAG "SchedPolicy" + #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> +#include <cutils/sched_policy.h> +#include <cutils/log.h> -#define LOG_TAG "SchedPolicy" -#include "cutils/log.h" +/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged. + * Call this any place a SchedPolicy is used as an input parameter. + * Returns the possibly re-mapped policy. + */ +static inline SchedPolicy _policy(SchedPolicy p) +{ + return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p; +} -#ifdef HAVE_SCHED_H -#ifdef HAVE_PTHREADS +#if defined(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS) #include <sched.h> #include <pthread.h> -#include <cutils/sched_policy.h> - #ifndef SCHED_NORMAL #define SCHED_NORMAL 0 #endif @@ -44,28 +51,45 @@ #define POLICY_DEBUG 0 +#define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM) + static pthread_once_t the_once = PTHREAD_ONCE_INIT; static int __sys_supports_schedgroups = -1; // File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error. -static int normal_cgroup_fd = -1; static int bg_cgroup_fd = -1; +static int fg_cgroup_fd = -1; +#if CAN_SET_SP_SYSTEM +static int system_cgroup_fd = -1; +#endif /* Add tid to the scheduling group defined by the policy */ static int add_tid_to_cgroup(int tid, SchedPolicy policy) { int fd; - if (policy == SP_BACKGROUND) { + switch (policy) { + case SP_BACKGROUND: fd = bg_cgroup_fd; - } else { - fd = normal_cgroup_fd; + break; + case SP_FOREGROUND: + case SP_AUDIO_APP: + case SP_AUDIO_SYS: + fd = fg_cgroup_fd; + break; +#if CAN_SET_SP_SYSTEM + case SP_SYSTEM: + fd = system_cgroup_fd; + break; +#endif + default: + fd = -1; + break; } if (fd < 0) { - SLOGE("add_tid_to_cgroup failed; background=%d\n", - policy == SP_BACKGROUND ? 1 : 0); + SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy); return -1; } @@ -86,8 +110,8 @@ static int add_tid_to_cgroup(int tid, SchedPolicy policy) */ if (errno == ESRCH) return 0; - SLOGW("add_tid_to_cgroup failed to write '%s' (%s); background=%d\n", - ptr, strerror(errno), policy == SP_BACKGROUND ? 1 : 0); + SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n", + ptr, strerror(errno), policy); return -1; } @@ -99,14 +123,22 @@ static void __initialize(void) { if (!access("/dev/cpuctl/tasks", F_OK)) { __sys_supports_schedgroups = 1; +#if CAN_SET_SP_SYSTEM filename = "/dev/cpuctl/tasks"; - normal_cgroup_fd = open(filename, O_WRONLY); - if (normal_cgroup_fd < 0) { + system_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (system_cgroup_fd < 0) { + SLOGV("open of %s failed: %s\n", filename, strerror(errno)); + } +#endif + + filename = "/dev/cpuctl/apps/tasks"; + fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (fg_cgroup_fd < 0) { SLOGE("open of %s failed: %s\n", filename, strerror(errno)); } - filename = "/dev/cpuctl/bg_non_interactive/tasks"; - bg_cgroup_fd = open(filename, O_WRONLY); + filename = "/dev/cpuctl/apps/bg_non_interactive/tasks"; + bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); if (bg_cgroup_fd < 0) { SLOGE("open of %s failed: %s\n", filename, strerror(errno)); } @@ -189,6 +221,11 @@ static int getSchedulerGroup(int tid, char* buf, size_t bufLen) int get_sched_policy(int tid, SchedPolicy *policy) { +#ifdef HAVE_GETTID + if (tid == 0) { + tid = gettid(); + } +#endif pthread_once(&the_once, __initialize); if (__sys_supports_schedgroups) { @@ -196,9 +233,11 @@ int get_sched_policy(int tid, SchedPolicy *policy) if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0) return -1; if (grpBuf[0] == '\0') { - *policy = SP_FOREGROUND; - } else if (!strcmp(grpBuf, "bg_non_interactive")) { + *policy = SP_SYSTEM; + } else if (!strcmp(grpBuf, "apps/bg_non_interactive")) { *policy = SP_BACKGROUND; + } else if (!strcmp(grpBuf, "apps")) { + *policy = SP_FOREGROUND; } else { errno = ERANGE; return -1; @@ -221,6 +260,12 @@ int get_sched_policy(int tid, SchedPolicy *policy) int set_sched_policy(int tid, SchedPolicy policy) { +#ifdef HAVE_GETTID + if (tid == 0) { + tid = gettid(); + } +#endif + policy = _policy(policy); pthread_once(&the_once, __initialize); #if POLICY_DEBUG @@ -246,12 +291,21 @@ int set_sched_policy(int tid, SchedPolicy policy) strncpy(thread_name, p, (q-p)); } - if (policy == SP_BACKGROUND) { + switch (policy) { + case SP_BACKGROUND: SLOGD("vvv tid %d (%s)", tid, thread_name); - } else if (policy == SP_FOREGROUND) { + break; + case SP_FOREGROUND: + case SP_AUDIO_APP: + case SP_AUDIO_SYS: SLOGD("^^^ tid %d (%s)", tid, thread_name); - } else { + break; + case SP_SYSTEM: + SLOGD("/// tid %d (%s)", tid, thread_name); + break; + default: SLOGD("??? tid %d (%s)", tid, thread_name); + break; } #endif @@ -273,5 +327,36 @@ int set_sched_policy(int tid, SchedPolicy policy) return 0; } -#endif /* HAVE_PTHREADS */ -#endif /* HAVE_SCHED_H */ +#else + +/* Stubs for non-Android targets. */ + +int set_sched_policy(int tid, SchedPolicy policy) +{ + return 0; +} + +int get_sched_policy(int tid, SchedPolicy *policy) +{ + *policy = SP_SYSTEM_DEFAULT; + return 0; +} + +#endif + +const char *get_sched_policy_name(SchedPolicy policy) +{ + policy = _policy(policy); + static const char * const strings[SP_CNT] = { + [SP_BACKGROUND] = "bg", + [SP_FOREGROUND] = "fg", + [SP_SYSTEM] = " ", + [SP_AUDIO_APP] = "aa", + [SP_AUDIO_SYS] = "as", + }; + if ((policy < SP_CNT) && (strings[policy] != NULL)) + return strings[policy]; + else + return "error"; +} + diff --git a/libcutils/str_parms.c b/libcutils/str_parms.c index 14fecec..9e1d2dc 100644 --- a/libcutils/str_parms.c +++ b/libcutils/str_parms.c @@ -70,19 +70,57 @@ err: return NULL; } +struct remove_ctxt { + struct str_parms *str_parms; + const char *key; +}; + static bool remove_pair(void *key, void *value, void *context) { - struct str_parms *str_parms = context; + struct remove_ctxt *ctxt = context; + bool should_continue; + + /* + * - if key is not supplied, then we are removing all entries, + * so remove key and continue (i.e. return true) + * - if key is supplied and matches, then remove it and don't + * continue (return false). Otherwise, return true and keep searching + * for key. + * + */ + if (!ctxt->key) { + should_continue = true; + goto do_remove; + } else if (!strcmp(ctxt->key, key)) { + should_continue = false; + goto do_remove; + } - hashmapRemove(str_parms->map, key); + return true; + +do_remove: + hashmapRemove(ctxt->str_parms->map, key); free(key); free(value); - return true; + return should_continue; +} + +void str_parms_del(struct str_parms *str_parms, const char *key) +{ + struct remove_ctxt ctxt = { + .str_parms = str_parms, + .key = key, + }; + hashmapForEach(str_parms->map, remove_pair, &ctxt); } void str_parms_destroy(struct str_parms *str_parms) { - hashmapForEach(str_parms->map, remove_pair, str_parms); + struct remove_ctxt ctxt = { + .str_parms = str_parms, + }; + + hashmapForEach(str_parms->map, remove_pair, &ctxt); hashmapFree(str_parms->map); free(str_parms); } @@ -128,8 +166,10 @@ struct str_parms *str_parms_create_str(const char *_string) /* if we replaced a value, free it */ old_val = hashmapPut(str_parms->map, key, value); - if (old_val) + if (old_val) { free(old_val); + free(key); + } items++; next_pair: @@ -149,24 +189,23 @@ err_create_str_parms: return NULL; } -void str_parms_del(struct str_parms *str_parms, const char *key) -{ - hashmapRemove(str_parms->map, (void *)key); -} - int str_parms_add_str(struct str_parms *str_parms, const char *key, const char *value) { void *old_val; - char *tmp; + void *tmp_key; + void *tmp_val; - tmp = strdup(value); - old_val = hashmapPut(str_parms->map, (void *)key, tmp); + tmp_key = strdup(key); + tmp_val = strdup(value); + old_val = hashmapPut(str_parms->map, tmp_key, tmp_val); if (old_val) { free(old_val); + free(tmp_key); } else if (errno == ENOMEM) { - free(tmp); + free(tmp_key); + free(tmp_val); return -ENOMEM; } return 0; @@ -298,6 +337,9 @@ static void test_str_parms_str(const char *str) int ret; str_parms = str_parms_create_str(str); + str_parms_add_str(str_parms, "dude", "woah"); + str_parms_add_str(str_parms, "dude", "woah"); + str_parms_del(str_parms, "dude"); str_parms_dump(str_parms); out_str = str_parms_to_str(str_parms); str_parms_destroy(str_parms); @@ -323,6 +365,7 @@ int main(void) test_str_parms_str("foo=bar;baz="); test_str_parms_str("foo=bar;baz=bat"); test_str_parms_str("foo=bar;baz=bat;"); + test_str_parms_str("foo=bar;baz=bat;foo=bar"); return 0; } diff --git a/libcutils/tzstrftime.c b/libcutils/tzstrftime.c index e37d79a..e4f54df 100644 --- a/libcutils/tzstrftime.c +++ b/libcutils/tzstrftime.c @@ -8,6 +8,7 @@ static char elsieid[] = "@(#)strftime.c 8.1"; #endif /* !defined NOID */ #endif /* !defined lint */ +#include <stdio.h> #include <time.h> #include <tzfile.h> #include <limits.h> diff --git a/libcutils/uevent.c b/libcutils/uevent.c index 4add29c..97a81e3 100644 --- a/libcutils/uevent.c +++ b/libcutils/uevent.c @@ -29,7 +29,24 @@ /** * Like recv(), but checks that messages actually originate from the kernel. */ -ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) { +ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) +{ + uid_t user = -1; + return uevent_kernel_multicast_uid_recv(socket, buffer, length, &user); +} + +/** + * Like the above, but passes a uid_t in by reference. In the event that this + * fails due to a bad uid check, the uid_t will be set to the uid of the + * socket's peer. + * + * If this method rejects a netlink message from outside the kernel, it + * returns -1, sets errno to EIO, and sets "user" to the UID associated with the + * message. If the peer UID cannot be determined, "user" is set to -1." + */ +ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, + size_t length, uid_t *user) +{ struct iovec iov = { buffer, length }; struct sockaddr_nl addr; char control[CMSG_SPACE(sizeof(struct ucred))]; @@ -43,16 +60,12 @@ ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) { 0, }; + *user = -1; ssize_t n = recvmsg(socket, &hdr, 0); if (n <= 0) { return n; } - if (addr.nl_groups == 0 || addr.nl_pid != 0) { - /* ignoring non-kernel or unicast netlink message */ - goto out; - } - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { /* ignoring netlink message with no sender credentials */ @@ -60,11 +73,17 @@ ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) { } struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg); + *user = cred->uid; if (cred->uid != 0) { /* ignoring netlink message from non-root user */ goto out; } + if (addr.nl_groups == 0 || addr.nl_pid != 0) { + /* ignoring non-kernel or unicast netlink message */ + goto out; + } + return n; out: diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk index 9decbb9..b5d83fa 100644 --- a/libdiskconfig/Android.mk +++ b/libdiskconfig/Android.mk @@ -19,7 +19,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(commonSources) LOCAL_MODULE := libdiskconfig_host LOCAL_MODULE_TAGS := optional -LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE include $(BUILD_HOST_STATIC_LIBRARY) endif # HOST_OS == linux diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c index 703484c..b89d382 100644 --- a/libdiskconfig/config_mbr.c +++ b/libdiskconfig/config_mbr.c @@ -152,7 +152,7 @@ mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba, /* we are going to write the ebr at the current LBA, and then bump the * lba counter since that is where the logical data partition will start */ - item->offset = (*lba) * dinfo->sect_size; + item->offset = ((loff_t)(*lba)) * dinfo->sect_size; (*lba)++; ebr = (struct pc_boot_record *) &item->data; diff --git a/libion/Android.mk b/libion/Android.mk new file mode 100644 index 0000000..5121fee --- /dev/null +++ b/libion/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := ion.c +LOCAL_MODULE := libion +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := liblog +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := ion.c ion_test.c +LOCAL_MODULE := iontest +LOCAL_MODULE_TAGS := optional tests +LOCAL_SHARED_LIBRARIES := liblog +include $(BUILD_EXECUTABLE) diff --git a/libion/ion.c b/libion/ion.c new file mode 100644 index 0000000..dbeac23 --- /dev/null +++ b/libion/ion.c @@ -0,0 +1,134 @@ +/* + * ion.c + * + * Memory Allocator functions for ion + * + * Copyright 2011 Google, Inc + * + * 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. + */ +#define LOG_TAG "ion" + +#include <cutils/log.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> + +#include <linux/ion.h> +#include <ion/ion.h> + +int ion_open() +{ + int fd = open("/dev/ion", O_RDWR); + if (fd < 0) + ALOGE("open /dev/ion failed!\n"); + return fd; +} + +int ion_close(int fd) +{ + return close(fd); +} + +static int ion_ioctl(int fd, int req, void *arg) +{ + int ret = ioctl(fd, req, arg); + if (ret < 0) { + ALOGE("ioctl %x failed with code %d: %s\n", req, + ret, strerror(errno)); + return -errno; + } + return ret; +} + +int ion_alloc(int fd, size_t len, size_t align, unsigned int flags, + struct ion_handle **handle) +{ + int ret; + struct ion_allocation_data data = { + .len = len, + .align = align, + .flags = flags, + }; + + ret = ion_ioctl(fd, ION_IOC_ALLOC, &data); + if (ret < 0) + return ret; + *handle = data.handle; + return ret; +} + +int ion_free(int fd, struct ion_handle *handle) +{ + struct ion_handle_data data = { + .handle = handle, + }; + return ion_ioctl(fd, ION_IOC_FREE, &data); +} + +int ion_map(int fd, struct ion_handle *handle, size_t length, int prot, + int flags, off_t offset, unsigned char **ptr, int *map_fd) +{ + struct ion_fd_data data = { + .handle = handle, + }; + + int ret = ion_ioctl(fd, ION_IOC_MAP, &data); + if (ret < 0) + return ret; + *map_fd = data.fd; + if (*map_fd < 0) { + ALOGE("map ioctl returned negative fd\n"); + return -EINVAL; + } + *ptr = mmap(NULL, length, prot, flags, *map_fd, offset); + if (*ptr == MAP_FAILED) { + ALOGE("mmap failed: %s\n", strerror(errno)); + return -errno; + } + return ret; +} + +int ion_share(int fd, struct ion_handle *handle, int *share_fd) +{ + int map_fd; + struct ion_fd_data data = { + .handle = handle, + }; + + int ret = ion_ioctl(fd, ION_IOC_SHARE, &data); + if (ret < 0) + return ret; + *share_fd = data.fd; + if (*share_fd < 0) { + ALOGE("share ioctl returned negative fd\n"); + return -EINVAL; + } + return ret; +} + +int ion_import(int fd, int share_fd, struct ion_handle **handle) +{ + struct ion_fd_data data = { + .fd = share_fd, + }; + + int ret = ion_ioctl(fd, ION_IOC_IMPORT, &data); + if (ret < 0) + return ret; + *handle = data.handle; + return ret; +} diff --git a/libion/ion_test.c b/libion/ion_test.c new file mode 100644 index 0000000..3f2d7cc --- /dev/null +++ b/libion/ion_test.c @@ -0,0 +1,282 @@ +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <ion/ion.h> +#include <linux/ion.h> +#include <linux/omap_ion.h> + +size_t len = 1024*1024, align = 0; +int prot = PROT_READ | PROT_WRITE; +int map_flags = MAP_SHARED; +int alloc_flags = 0; +int test = -1; +size_t width = 1024*1024, height = 1024*1024; +size_t stride; + +int _ion_alloc_test(int *fd, struct ion_handle **handle) +{ + int ret; + + *fd = ion_open(); + if (*fd < 0) + return *fd; + + ret = ion_alloc(*fd, len, align, alloc_flags, handle); + + if (ret) + printf("%s failed: %s\n", __func__, strerror(ret)); + return ret; +} + +void ion_alloc_test() +{ + int fd, ret; + struct ion_handle *handle; + + if(_ion_alloc_test(&fd, &handle)) + return; + + ret = ion_free(fd, handle); + if (ret) { + printf("%s failed: %s %p\n", __func__, strerror(ret), handle); + return; + } + ion_close(fd); + printf("ion alloc test: passed\n"); +} + +void ion_map_test() +{ + int fd, map_fd, ret; + size_t i; + struct ion_handle *handle; + unsigned char *ptr; + + if(_ion_alloc_test(&fd, &handle)) + return; + + ret = ion_map(fd, handle, len, prot, map_flags, 0, &ptr, &map_fd); + if (ret) + return; + + for (i = 0; i < len; i++) { + ptr[i] = (unsigned char)i; + } + for (i = 0; i < len; i++) + if (ptr[i] != (unsigned char)i) + printf("%s failed wrote %d read %d from mapped " + "memory\n", __func__, i, ptr[i]); + /* clean up properly */ + ret = ion_free(fd, handle); + ion_close(fd); + munmap(ptr, len); + close(map_fd); + + _ion_alloc_test(&fd, &handle); + close(fd); + +#if 0 + munmap(ptr, len); + close(map_fd); + ion_close(fd); + + _ion_alloc_test(len, align, flags, &fd, &handle); + close(map_fd); + ret = ion_map(fd, handle, len, prot, flags, 0, &ptr, &map_fd); + /* don't clean up */ +#endif +} + +void ion_share_test() + +{ + struct ion_handle *handle; + int sd[2]; + int num_fd = 1; + struct iovec count_vec = { + .iov_base = &num_fd, + .iov_len = sizeof num_fd, + }; + char buf[CMSG_SPACE(sizeof(int))]; + socketpair(AF_UNIX, SOCK_STREAM, 0, sd); + if (fork()) { + struct msghdr msg = { + .msg_control = buf, + .msg_controllen = sizeof buf, + .msg_iov = &count_vec, + .msg_iovlen = 1, + }; + + struct cmsghdr *cmsg; + int fd, share_fd, ret; + char *ptr; + /* parent */ + if(_ion_alloc_test(&fd, &handle)) + return; + ret = ion_share(fd, handle, &share_fd); + if (ret) + printf("share failed %s\n", strerror(errno)); + ptr = mmap(NULL, len, prot, map_flags, share_fd, 0); + if (ptr == MAP_FAILED) { + return; + } + strcpy(ptr, "master"); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + *(int *)CMSG_DATA(cmsg) = share_fd; + /* send the fd */ + printf("master? [%10s] should be [master]\n", ptr); + printf("master sending msg 1\n"); + sendmsg(sd[0], &msg, 0); + if (recvmsg(sd[0], &msg, 0) < 0) + perror("master recv msg 2"); + printf("master? [%10s] should be [child]\n", ptr); + + /* send ping */ + sendmsg(sd[0], &msg, 0); + printf("master->master? [%10s]\n", ptr); + if (recvmsg(sd[0], &msg, 0) < 0) + perror("master recv 1"); + } else { + struct msghdr msg; + struct cmsghdr *cmsg; + char* ptr; + int fd, recv_fd; + char* child_buf[100]; + /* child */ + struct iovec count_vec = { + .iov_base = child_buf, + .iov_len = sizeof child_buf, + }; + + struct msghdr child_msg = { + .msg_control = buf, + .msg_controllen = sizeof buf, + .msg_iov = &count_vec, + .msg_iovlen = 1, + }; + + if (recvmsg(sd[1], &child_msg, 0) < 0) + perror("child recv msg 1"); + cmsg = CMSG_FIRSTHDR(&child_msg); + if (cmsg == NULL) { + printf("no cmsg rcvd in child"); + return; + } + recv_fd = *(int*)CMSG_DATA(cmsg); + if (recv_fd < 0) { + printf("could not get recv_fd from socket"); + return; + } + printf("child %d\n", recv_fd); + fd = ion_open(); + ptr = mmap(NULL, len, prot, map_flags, recv_fd, 0); + if (ptr == MAP_FAILED) { + return; + } + printf("child? [%10s] should be [master]\n", ptr); + strcpy(ptr, "child"); + printf("child sending msg 2\n"); + sendmsg(sd[1], &child_msg, 0); + } +} + +int main(int argc, char* argv[]) { + int c; + enum tests { + ALLOC_TEST = 0, MAP_TEST, SHARE_TEST, + }; + + while (1) { + static struct option opts[] = { + {"alloc", no_argument, 0, 'a'}, + {"alloc_flags", required_argument, 0, 'f'}, + {"map", no_argument, 0, 'm'}, + {"share", no_argument, 0, 's'}, + {"len", required_argument, 0, 'l'}, + {"align", required_argument, 0, 'g'}, + {"map_flags", required_argument, 0, 'z'}, + {"prot", required_argument, 0, 'p'}, + {"width", required_argument, 0, 'w'}, + {"height", required_argument, 0, 'h'}, + }; + int i = 0; + c = getopt_long(argc, argv, "af:h:l:mr:stw:", opts, &i); + if (c == -1) + break; + + switch (c) { + case 'l': + len = atol(optarg); + break; + case 'g': + align = atol(optarg); + break; + case 'z': + map_flags = 0; + map_flags |= strstr(optarg, "PROT_EXEC") ? + PROT_EXEC : 0; + map_flags |= strstr(optarg, "PROT_READ") ? + PROT_READ: 0; + map_flags |= strstr(optarg, "PROT_WRITE") ? + PROT_WRITE: 0; + map_flags |= strstr(optarg, "PROT_NONE") ? + PROT_NONE: 0; + break; + case 'p': + prot = 0; + prot |= strstr(optarg, "MAP_PRIVATE") ? + MAP_PRIVATE : 0; + prot |= strstr(optarg, "MAP_SHARED") ? + MAP_PRIVATE : 0; + break; + case 'f': + alloc_flags = atol(optarg); + break; + case 'a': + test = ALLOC_TEST; + break; + case 'm': + test = MAP_TEST; + break; + case 's': + test = SHARE_TEST; + break; + case 'w': + width = atol(optarg); + break; + case 'h': + height = atol(optarg); + break; + } + } + printf("test %d, len %u, width %u, height %u align %u, " + "map_flags %d, prot %d, alloc_flags %d\n", test, len, width, + height, align, map_flags, prot, alloc_flags); + switch (test) { + case ALLOC_TEST: + ion_alloc_test(); + break; + case MAP_TEST: + ion_map_test(); + break; + case SHARE_TEST: + ion_share_test(); + break; + default: + printf("must specify a test (alloc, map, share)\n"); + } + return 0; +} diff --git a/liblog/Android.mk b/liblog/Android.mk index bd2590e..be5cec2 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -41,7 +41,7 @@ endif liblog_host_sources := $(liblog_sources) fake_log_device.c -# Static library for host +# Shared and static library for host # ======================================================== LOCAL_MODULE := liblog LOCAL_SRC_FILES := $(liblog_host_sources) @@ -49,6 +49,11 @@ LOCAL_LDLIBS := -lpthread LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 include $(BUILD_HOST_STATIC_LIBRARY) +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_WHOLE_STATIC_LIBRARIES := liblog +include $(BUILD_HOST_SHARED_LIBRARY) + # Static library for host, 64-bit # ======================================================== diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index f8b7254..df43299 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -398,7 +398,7 @@ static void showLog(LogState *state, break; case FORMAT_THREAD: prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c(%5d:%p) ", priChar, pid, (void*)tid); + "%c(%5d:%5d) ", priChar, pid, tid); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_RAW: @@ -417,8 +417,8 @@ static void showLog(LogState *state, break; case FORMAT_LONG: prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "[ %s %5d:%p %c/%-8s ]\n", - timeBuf, pid, (void*)tid, priChar, tag); + "[ %s %5d:%5d %c/%-8s ]\n", + timeBuf, pid, tid, priChar, tag); strcpy(suffixBuf, "\n\n"); suffixLen = 2; break; default: diff --git a/liblog/logprint.c b/liblog/logprint.c index 8366c94..6fac84b 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -673,7 +673,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, if (inCount != 0) { fprintf(stderr, - "Warning: leftover binary log data (%d bytes)\n", inCount); + "Warning: leftover binary log data (%zu bytes)\n", inCount); } /* @@ -753,7 +753,7 @@ char *android_log_formatLogLine ( break; case FORMAT_THREAD: prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c(%5d:%p) ", priChar, entry->pid, (void*)entry->tid); + "%c(%5d:%5d) ", priChar, entry->pid, entry->tid); strcpy(suffixBuf, "\n"); suffixLen = 1; break; @@ -773,15 +773,15 @@ char *android_log_formatLogLine ( case FORMAT_THREADTIME: prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, - (int)entry->pid, (int)entry->tid, priChar, entry->tag); + entry->pid, entry->tid, priChar, entry->tag); strcpy(suffixBuf, "\n"); suffixLen = 1; break; case FORMAT_LONG: prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "[ %s.%03ld %5d:%p %c/%-8s ]\n", + "[ %s.%03ld %5d:%5d %c/%-8s ]\n", timeBuf, entry->tv_nsec / 1000000, entry->pid, - (void*)entry->tid, priChar, entry->tag); + entry->tid, priChar, entry->tag); strcpy(suffixBuf, "\n\n"); suffixLen = 2; prefixSuffixIsHeaderFooter = 1; diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 1e08eb6..903a1db 100755..100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -33,8 +33,28 @@ static const int NAP_TIME = 200; /* wait for 200ms at a time */ /* when polling for property values */ static const char DAEMON_NAME_RENEW[] = "iprenew"; static char errmsg[100]; -/* interface suffix on dhcpcd */ -#define MAX_DAEMON_SUFFIX 25 +/* interface length for dhcpcd daemon start (dhcpcd_<interface> as defined in init.rc file) + * or for filling up system properties dhcpcd.<interface>.ipaddress, dhcpcd.<interface>.dns1 + * and other properties on a successful bind + */ +#define MAX_INTERFACE_LENGTH 25 + +/* + * P2p interface names increase sequentially p2p-p2p0-1, p2p-p2p0-2.. after + * group formation. This does not work well with system properties which can quickly + * exhaust or for specifiying a dhcp start target in init which requires + * interface to be pre-defined in init.rc file. + * + * This function returns a common string p2p for all p2p interfaces. + */ +void get_p2p_interface_replacement(const char *interface, char *p2p_interface) { + /* Use p2p for any interface starting with p2p. */ + if (strncmp(interface, "p2p",3) == 0) { + strncpy(p2p_interface, "p2p", MAX_INTERFACE_LENGTH); + } else { + strncpy(p2p_interface, interface, MAX_INTERFACE_LENGTH); + } +} /* * Wait for a system property to be assigned a specified value. @@ -70,18 +90,23 @@ static int fill_ip_info(const char *interface, char *dns1, char *dns2, char *server, - uint32_t *lease) + uint32_t *lease, + char *vendorInfo) { char prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX]; + /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */ + char p2p_interface[MAX_INTERFACE_LENGTH]; + + get_p2p_interface_replacement(interface, p2p_interface); - snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, interface); + snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, p2p_interface); property_get(prop_name, ipaddr, NULL); - snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, interface); + snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, p2p_interface); property_get(prop_name, gateway, NULL); - snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, interface); + snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, p2p_interface); property_get(prop_name, server, NULL); //TODO: Handle IPv6 when we change system property usage @@ -90,7 +115,7 @@ static int fill_ip_info(const char *interface, strncpy(gateway, server, PROPERTY_VALUE_MAX); } - snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, interface); + snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, p2p_interface); if (property_get(prop_name, prop_value, NULL)) { int p; // this conversion is v4 only, but this dhcp client is v4 only anyway @@ -112,16 +137,21 @@ static int fill_ip_info(const char *interface, } *prefixLength = p; } - snprintf(prop_name, sizeof(prop_name), "%s.%s.dns1", DHCP_PROP_NAME_PREFIX, interface); + snprintf(prop_name, sizeof(prop_name), "%s.%s.dns1", DHCP_PROP_NAME_PREFIX, p2p_interface); property_get(prop_name, dns1, NULL); - snprintf(prop_name, sizeof(prop_name), "%s.%s.dns2", DHCP_PROP_NAME_PREFIX, interface); + snprintf(prop_name, sizeof(prop_name), "%s.%s.dns2", DHCP_PROP_NAME_PREFIX, p2p_interface); property_get(prop_name, dns2, NULL); - snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, interface); + snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, p2p_interface); if (property_get(prop_name, prop_value, NULL)) { *lease = atol(prop_value); } + + snprintf(prop_name, sizeof(prop_name), "%s.%s.vendorInfo", DHCP_PROP_NAME_PREFIX, + p2p_interface); + property_get(prop_name, vendorInfo, NULL); + return 0; } @@ -133,18 +163,14 @@ static const char *ipaddr_to_string(in_addr_t addr) return inet_ntoa(in_addr); } -void get_daemon_suffix(const char *interface, char *daemon_suffix) { - /* Use p2p suffix for any p2p interface. */ - if (strncmp(interface, "p2p",3) == 0) { - sprintf(daemon_suffix, "p2p"); - } else { - snprintf(daemon_suffix, MAX_DAEMON_SUFFIX, "%s", interface); - } -} - /* * Start the dhcp client daemon, and wait for it to finish * configuring the interface. + * + * The device init.rc file needs a corresponding entry for this work. + * + * Example: + * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL */ int dhcp_do_request(const char *interface, char *ipaddr, @@ -153,7 +179,8 @@ int dhcp_do_request(const char *interface, char *dns1, char *dns2, char *server, - uint32_t *lease) + uint32_t *lease, + char *vendorInfo) { char result_prop_name[PROPERTY_KEY_MAX]; char daemon_prop_name[PROPERTY_KEY_MAX]; @@ -161,27 +188,28 @@ int dhcp_do_request(const char *interface, char daemon_cmd[PROPERTY_VALUE_MAX * 2]; const char *ctrl_prop = "ctl.start"; const char *desired_status = "running"; - char daemon_suffix[MAX_DAEMON_SUFFIX]; + /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */ + char p2p_interface[MAX_INTERFACE_LENGTH]; - get_daemon_suffix(interface, daemon_suffix); + get_p2p_interface_replacement(interface, p2p_interface); snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, - interface); + p2p_interface); snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", DAEMON_PROP_NAME, - daemon_suffix); + p2p_interface); /* Erase any previous setting of the dhcp result property */ property_set(result_prop_name, ""); /* Start the daemon and wait until it's ready */ if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0')) - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, daemon_suffix, + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, p2p_interface, prop_value, interface); else - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, daemon_suffix, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, p2p_interface, interface); memset(prop_value, '\0', PROPERTY_VALUE_MAX); property_set(ctrl_prop, daemon_cmd); if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) { @@ -202,8 +230,8 @@ int dhcp_do_request(const char *interface, } if (strcmp(prop_value, "ok") == 0) { char dns_prop_name[PROPERTY_KEY_MAX]; - if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns1, dns2, server, lease) - == -1) { + if (fill_ip_info(interface, ipaddr, gateway, prefixLength, + dns1, dns2, server, lease, vendorInfo) == -1) { return -1; } @@ -231,19 +259,19 @@ int dhcp_stop(const char *interface) const char *ctrl_prop = "ctl.stop"; const char *desired_status = "stopped"; - char daemon_suffix[MAX_DAEMON_SUFFIX]; + char p2p_interface[MAX_INTERFACE_LENGTH]; - get_daemon_suffix(interface, daemon_suffix); + get_p2p_interface_replacement(interface, p2p_interface); snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, - interface); + p2p_interface); snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", DAEMON_PROP_NAME, - daemon_suffix); + p2p_interface); - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, daemon_suffix); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface); /* Stop the daemon and wait until it's reported to be stopped */ property_set(ctrl_prop, daemon_cmd); @@ -264,15 +292,15 @@ int dhcp_release_lease(const char *interface) const char *ctrl_prop = "ctl.stop"; const char *desired_status = "stopped"; - char daemon_suffix[MAX_DAEMON_SUFFIX]; + char p2p_interface[MAX_INTERFACE_LENGTH]; - get_daemon_suffix(interface, daemon_suffix); + get_p2p_interface_replacement(interface, p2p_interface); snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s", DAEMON_PROP_NAME, - daemon_suffix); + p2p_interface); - snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, daemon_suffix); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface); /* Stop the daemon and wait until it's reported to be stopped */ property_set(ctrl_prop, daemon_cmd); @@ -287,37 +315,41 @@ char *dhcp_get_errmsg() { } /** - * Run WiMAX dhcp renew service. - * "wimax_renew" service shoud be included in init.rc. + * The device init.rc file needs a corresponding entry. + * + * Example: + * service iprenew_<interface> /system/bin/dhcpcd -n + * */ int dhcp_do_request_renew(const char *interface, - in_addr_t *ipaddr, - in_addr_t *gateway, + char *ipaddr, + char *gateway, uint32_t *prefixLength, - in_addr_t *dns1, - in_addr_t *dns2, - in_addr_t *server, - uint32_t *lease) + char *dns1, + char *dns2, + char *server, + uint32_t *lease, + char *vendorInfo) { char result_prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; char daemon_cmd[PROPERTY_VALUE_MAX * 2]; const char *ctrl_prop = "ctl.start"; - char daemon_suffix[MAX_DAEMON_SUFFIX]; + char p2p_interface[MAX_INTERFACE_LENGTH]; - get_daemon_suffix(interface, daemon_suffix); + get_p2p_interface_replacement(interface, p2p_interface); snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", DHCP_PROP_NAME_PREFIX, - interface); + p2p_interface); /* Erase any previous setting of the dhcp result property */ property_set(result_prop_name, ""); /* Start the renew daemon and wait until it's ready */ snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW, - daemon_suffix, interface); + p2p_interface, interface); memset(prop_value, '\0', PROPERTY_VALUE_MAX); property_set(ctrl_prop, daemon_cmd); @@ -333,7 +365,8 @@ int dhcp_do_request_renew(const char *interface, return -1; } if (strcmp(prop_value, "ok") == 0) { - fill_ip_info(interface, ipaddr, gateway, prefixLength, dns1, dns2, server, lease); + fill_ip_info(interface, ipaddr, gateway, prefixLength, + dns1, dns2, server, lease, vendorInfo); return 0; } else { snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value); diff --git a/libsparse/Android.mk b/libsparse/Android.mk new file mode 100644 index 0000000..69b52c3 --- /dev/null +++ b/libsparse/Android.mk @@ -0,0 +1,101 @@ +# Copyright 2010 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) + +libsparse_src_files := \ + backed_block.c \ + output_file.c \ + sparse.c \ + sparse_crc32.c \ + sparse_err.c \ + sparse_read.c + +include $(CLEAR_VARS) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_SRC_FILES := $(libsparse_src_files) +LOCAL_MODULE := libsparse +LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_LIBRARIES := libz +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include external/zlib + +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_SRC_FILES := $(libsparse_src_files) +LOCAL_MODULE := libsparse +LOCAL_MODULE_TAGS := optional +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include external/zlib +LOCAL_SHARED_LIBRARIES := libz + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_SRC_FILES := $(libsparse_src_files) +LOCAL_MODULE := libsparse +LOCAL_MODULE_TAGS := optional +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include external/zlib +LOCAL_STATIC_LIBRARIES := libz + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := simg2img.c \ + sparse_crc32.c +LOCAL_MODULE := simg2img +LOCAL_MODULE_TAGS := debug +LOCAL_STATIC_LIBRARIES := libsparse libz + +include $(BUILD_HOST_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := simg2img.c \ + sparse_crc32.c +LOCAL_MODULE := simg2img +LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_LIBRARIES := libsparse libz + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := img2simg.c +LOCAL_MODULE := img2simg +LOCAL_MODULE_TAGS := debug +LOCAL_STATIC_LIBRARIES := libsparse libz + +include $(BUILD_HOST_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := img2simg.c +LOCAL_MODULE := img2simg +LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_LIBRARIES := libsparse libz + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := simg2simg.c +LOCAL_MODULE := simg2simg +LOCAL_MODULE_TAGS := debug +LOCAL_STATIC_LIBRARIES := libsparse libz + +include $(BUILD_HOST_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_MODULE := simg_dump.py +LOCAL_MODULE_TAGS := debug +LOCAL_SRC_FILES := simg_dump.py +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_IS_HOST_MODULE := true + +include $(BUILD_PREBUILT) diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c new file mode 100644 index 0000000..dfb217b --- /dev/null +++ b/libsparse/backed_block.c @@ -0,0 +1,400 @@ +/* + * 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 <assert.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "backed_block.h" +#include "sparse_defs.h" + +struct backed_block { + unsigned int block; + unsigned int len; + enum backed_block_type type; + union { + struct { + void *data; + } data; + struct { + char *filename; + int64_t offset; + } file; + struct { + int fd; + int64_t offset; + } fd; + struct { + uint32_t val; + } fill; + }; + struct backed_block *next; +}; + +struct backed_block_list { + struct backed_block *data_blocks; + struct backed_block *last_used; + unsigned int block_size; +}; + +struct backed_block *backed_block_iter_new(struct backed_block_list *bbl) +{ + return bbl->data_blocks; +} + +struct backed_block *backed_block_iter_next(struct backed_block *bb) +{ + return bb->next; +} + +unsigned int backed_block_len(struct backed_block *bb) +{ + return bb->len; +} + +unsigned int backed_block_block(struct backed_block *bb) +{ + return bb->block; +} + +void *backed_block_data(struct backed_block *bb) +{ + assert(bb->type == BACKED_BLOCK_DATA); + return bb->data.data; +} + +const char *backed_block_filename(struct backed_block *bb) +{ + assert(bb->type == BACKED_BLOCK_FILE); + return bb->file.filename; +} + +int backed_block_fd(struct backed_block *bb) +{ + assert(bb->type == BACKED_BLOCK_FD); + return bb->fd.fd; +} + +int64_t backed_block_file_offset(struct backed_block *bb) +{ + assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD); + if (bb->type == BACKED_BLOCK_FILE) { + return bb->file.offset; + } else { /* bb->type == BACKED_BLOCK_FD */ + return bb->fd.offset; + } +} + +uint32_t backed_block_fill_val(struct backed_block *bb) +{ + assert(bb->type == BACKED_BLOCK_FILL); + return bb->fill.val; +} + +enum backed_block_type backed_block_type(struct backed_block *bb) +{ + return bb->type; +} + +void backed_block_destroy(struct backed_block *bb) +{ + if (bb->type == BACKED_BLOCK_FILE) { + free(bb->file.filename); + } + + free(bb); +} + +struct backed_block_list *backed_block_list_new(unsigned int block_size) +{ + struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1); + b->block_size = block_size; + return b; +} + +void backed_block_list_destroy(struct backed_block_list *bbl) +{ + if (bbl->data_blocks) { + struct backed_block *bb = bbl->data_blocks; + while (bb) { + struct backed_block *next = bb->next; + backed_block_destroy(bb); + bb = next; + } + } + + free(bbl); +} + +void backed_block_list_move(struct backed_block_list *from, + struct backed_block_list *to, struct backed_block *start, + struct backed_block *end) +{ + struct backed_block *bb; + + if (start == NULL) { + start = from->data_blocks; + } + + if (!end) { + for (end = start; end && end->next; end = end->next) + ; + } + + if (start == NULL || end == NULL) { + return; + } + + from->last_used = NULL; + to->last_used = NULL; + if (from->data_blocks == start) { + from->data_blocks = end->next; + } else { + for (bb = from->data_blocks; bb; bb = bb->next) { + if (bb->next == start) { + bb->next = end->next; + break; + } + } + } + + if (!to->data_blocks) { + to->data_blocks = start; + end->next = NULL; + } else { + for (bb = to->data_blocks; bb; bb = bb->next) { + if (!bb->next || bb->next->block > start->block) { + end->next = bb->next; + bb->next = start; + break; + } + } + } +} + +/* may free b */ +static int merge_bb(struct backed_block_list *bbl, + struct backed_block *a, struct backed_block *b) +{ + unsigned int block_len; + + /* Block doesn't exist (possible if one block is the last block) */ + if (!a || !b) { + return -EINVAL; + } + + assert(a->block < b->block); + + /* Blocks are of different types */ + if (a->type != b->type) { + return -EINVAL; + } + + /* Blocks are not adjacent */ + block_len = a->len / bbl->block_size; /* rounds down */ + if (a->block + block_len != b->block) { + return -EINVAL; + } + + switch (a->type) { + case BACKED_BLOCK_DATA: + /* Don't support merging data for now */ + return -EINVAL; + case BACKED_BLOCK_FILL: + if (a->fill.val != b->fill.val) { + return -EINVAL; + } + break; + case BACKED_BLOCK_FILE: + if (a->file.filename != b->file.filename || + a->file.offset + a->len != b->file.offset) { + return -EINVAL; + } + break; + case BACKED_BLOCK_FD: + if (a->fd.fd != b->fd.fd || + a->fd.offset + a->len != b->fd.offset) { + return -EINVAL; + } + break; + } + + /* Blocks are compatible and adjacent, with a before b. Merge b into a, + * and free b */ + a->len += b->len; + a->next = b->next; + + backed_block_destroy(b); + + return 0; +} + +static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb) +{ + struct backed_block *bb; + + if (bbl->data_blocks == NULL) { + bbl->data_blocks = new_bb; + return 0; + } + + if (bbl->data_blocks->block > new_bb->block) { + new_bb->next = bbl->data_blocks; + bbl->data_blocks = new_bb; + return 0; + } + + /* Optimization: blocks are mostly queued in sequence, so save the + pointer to the last bb that was added, and start searching from + there if the next block number is higher */ + if (bbl->last_used && new_bb->block > bbl->last_used->block) + bb = bbl->last_used; + else + bb = bbl->data_blocks; + bbl->last_used = new_bb; + + for (; bb->next && bb->next->block < new_bb->block; bb = bb->next) + ; + + if (bb->next == NULL) { + bb->next = new_bb; + } else { + new_bb->next = bb->next; + bb->next = new_bb; + } + + merge_bb(bbl, new_bb, new_bb->next); + merge_bb(bbl, bb, new_bb); + + return 0; +} + +/* Queues a fill block of memory to be written to the specified data blocks */ +int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val, + unsigned int len, unsigned int block) +{ + struct backed_block *bb = calloc(1, sizeof(struct backed_block)); + if (bb == NULL) { + return -ENOMEM; + } + + bb->block = block; + bb->len = len; + bb->type = BACKED_BLOCK_FILL; + bb->fill.val = fill_val; + bb->next = NULL; + + return queue_bb(bbl, bb); +} + +/* Queues a block of memory to be written to the specified data blocks */ +int backed_block_add_data(struct backed_block_list *bbl, void *data, + unsigned int len, unsigned int block) +{ + struct backed_block *bb = calloc(1, sizeof(struct backed_block)); + if (bb == NULL) { + return -ENOMEM; + } + + bb->block = block; + bb->len = len; + bb->type = BACKED_BLOCK_DATA; + bb->data.data = data; + bb->next = NULL; + + return queue_bb(bbl, bb); +} + +/* Queues a chunk of a file on disk to be written to the specified data blocks */ +int backed_block_add_file(struct backed_block_list *bbl, const char *filename, + int64_t offset, unsigned int len, unsigned int block) +{ + struct backed_block *bb = calloc(1, sizeof(struct backed_block)); + if (bb == NULL) { + return -ENOMEM; + } + + bb->block = block; + bb->len = len; + bb->type = BACKED_BLOCK_FILE; + bb->file.filename = strdup(filename); + bb->file.offset = offset; + bb->next = NULL; + + return queue_bb(bbl, bb); +} + +/* Queues a chunk of a fd to be written to the specified data blocks */ +int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset, + unsigned int len, unsigned int block) +{ + struct backed_block *bb = calloc(1, sizeof(struct backed_block)); + if (bb == NULL) { + return -ENOMEM; + } + + bb->block = block; + bb->len = len; + bb->type = BACKED_BLOCK_FD; + bb->fd.fd = fd; + bb->fd.offset = offset; + bb->next = NULL; + + return queue_bb(bbl, bb); +} + +int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb, + unsigned int max_len) +{ + struct backed_block *new_bb; + + max_len = ALIGN_DOWN(max_len, bbl->block_size); + + if (bb->len <= max_len) { + return 0; + } + + new_bb = malloc(sizeof(struct backed_block)); + if (bb == NULL) { + return -ENOMEM; + } + + *new_bb = *bb; + + new_bb->len = bb->len - max_len; + new_bb->block = bb->block + max_len / bbl->block_size; + new_bb->next = bb->next; + bb->next = new_bb; + bb->len = max_len; + + switch (bb->type) { + case BACKED_BLOCK_DATA: + new_bb->data.data = (char *)bb->data.data + max_len; + break; + case BACKED_BLOCK_FILE: + new_bb->file.offset += max_len; + break; + case BACKED_BLOCK_FD: + new_bb->fd.offset += max_len; + break; + case BACKED_BLOCK_FILL: + break; + } + + return 0; +} diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h new file mode 100644 index 0000000..1a159be --- /dev/null +++ b/libsparse/backed_block.h @@ -0,0 +1,64 @@ +/* + * 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 _BACKED_BLOCK_H_ +#define _BACKED_BLOCK_H_ + +#include <stdint.h> + +struct backed_block_list; +struct backed_block; + +enum backed_block_type { + BACKED_BLOCK_DATA, + BACKED_BLOCK_FILE, + BACKED_BLOCK_FD, + BACKED_BLOCK_FILL, +}; + +int backed_block_add_data(struct backed_block_list *bbl, void *data, + unsigned int len, unsigned int block); +int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val, + unsigned int len, unsigned int block); +int backed_block_add_file(struct backed_block_list *bbl, const char *filename, + int64_t offset, unsigned int len, unsigned int block); +int backed_block_add_fd(struct backed_block_list *bbl, int fd, + int64_t offset, unsigned int len, unsigned int block); + +struct backed_block *backed_block_iter_new(struct backed_block_list *bbl); +struct backed_block *backed_block_iter_next(struct backed_block *bb); +unsigned int backed_block_len(struct backed_block *bb); +unsigned int backed_block_block(struct backed_block *bb); +void *backed_block_data(struct backed_block *bb); +const char *backed_block_filename(struct backed_block *bb); +int backed_block_fd(struct backed_block *bb); +int64_t backed_block_file_offset(struct backed_block *bb); +uint32_t backed_block_fill_val(struct backed_block *bb); +enum backed_block_type backed_block_type(struct backed_block *bb); +int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb, + unsigned int max_len); + +struct backed_block *backed_block_iter_new(struct backed_block_list *bbl); +struct backed_block *backed_block_iter_next(struct backed_block *bb); + +struct backed_block_list *backed_block_list_new(unsigned int block_size); +void backed_block_list_destroy(struct backed_block_list *bbl); + +void backed_block_list_move(struct backed_block_list *from, + struct backed_block_list *to, struct backed_block *start, + struct backed_block *end); + +#endif diff --git a/libsparse/img2simg.c b/libsparse/img2simg.c new file mode 100644 index 0000000..6b1caa5 --- /dev/null +++ b/libsparse/img2simg.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE 1 + +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sparse/sparse.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define off64_t off_t +#endif + +void usage() +{ + fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n"); +} + +int main(int argc, char *argv[]) +{ + int in; + int out; + unsigned int i; + int ret; + struct sparse_file *s; + unsigned int block_size = 4096; + off64_t len; + + if (argc < 3 || argc > 4) { + usage(); + exit(-1); + } + + if (argc == 4) { + block_size = atoi(argv[3]); + } + + if (block_size < 1024 || block_size % 4 != 0) { + usage(); + exit(-1); + } + + if (strcmp(argv[1], "-") == 0) { + in = STDIN_FILENO; + } else { + in = open(argv[1], O_RDONLY | O_BINARY); + if (in < 0) { + fprintf(stderr, "Cannot open input file %s\n", argv[1]); + exit(-1); + } + } + + if (strcmp(argv[2], "-") == 0) { + out = STDOUT_FILENO; + } else { + out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664); + if (out < 0) { + fprintf(stderr, "Cannot open output file %s\n", argv[2]); + exit(-1); + } + } + + len = lseek64(in, 0, SEEK_END); + lseek64(in, 0, SEEK_SET); + + s = sparse_file_new(block_size, len); + if (!s) { + fprintf(stderr, "Failed to create sparse file\n"); + exit(-1); + } + + sparse_file_verbose(s); + ret = sparse_file_read(s, in, false, false); + if (ret) { + fprintf(stderr, "Failed to read file\n"); + exit(-1); + } + + ret = sparse_file_write(s, out, false, true, false); + if (ret) { + fprintf(stderr, "Failed to write sparse file\n"); + exit(-1); + } + + close(in); + close(out); + + exit(0); +} diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h new file mode 100644 index 0000000..17d085c --- /dev/null +++ b/libsparse/include/sparse/sparse.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBSPARSE_SPARSE_H_ +#define _LIBSPARSE_SPARSE_H_ + +#include <stdbool.h> +#include <stdint.h> + +struct sparse_file; + +/** + * sparse_file_new - create a new sparse file cookie + * + * @block_size - minimum size of a chunk + * @len - size of the expanded sparse file. + * + * Creates a new sparse_file cookie that can be used to associate data + * blocks. Can later be written to a file with a variety of options. + * block_size specifies the minimum size of a chunk in the file. The maximum + * size of the file is 2**32 * block_size (16TB for 4k block size). + * + * Returns the sparse file cookie, or NULL on error. + */ +struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len); + +/** + * sparse_file_destroy - destroy a sparse file cookie + * + * @s - sparse file cookie + * + * Destroys a sparse file cookie. After destroy, all memory passed in to + * sparse_file_add_data can be freed by the caller + */ +void sparse_file_destroy(struct sparse_file *s); + +/** + * sparse_file_add_data - associate a data chunk with a sparse file + * + * @s - sparse file cookie + * @data - pointer to data block + * @len - length of the data block + * @block - offset in blocks into the sparse file to place the data chunk + * + * Associates a data chunk with a sparse file cookie. The region + * [block * block_size : block * block_size + len) must not already be used in + * the sparse file. If len is not a multiple of the block size the data + * will be padded with zeros. + * + * The data pointer must remain valid until the sparse file is closed or the + * data block is removed from the sparse file. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_add_data(struct sparse_file *s, + void *data, unsigned int len, unsigned int block); + +/** + * sparse_file_add_fill - associate a fill chunk with a sparse file + * + * @s - sparse file cookie + * @fill_val - 32 bit fill data + * @len - length of the fill block + * @block - offset in blocks into the sparse file to place the fill chunk + * + * Associates a chunk filled with fill_val with a sparse file cookie. + * The region [block * block_size : block * block_size + len) must not already + * be used in the sparse file. If len is not a multiple of the block size the + * data will be padded with zeros. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_add_fill(struct sparse_file *s, + uint32_t fill_val, unsigned int len, unsigned int block); + +/** + * sparse_file_add_file - associate a chunk of a file with a sparse file + * + * @s - sparse file cookie + * @filename - filename of the file to be copied + * @file_offset - offset into the copied file + * @len - length of the copied block + * @block - offset in blocks into the sparse file to place the file chunk + * + * Associates a chunk of an existing file with a sparse file cookie. + * The region [block * block_size : block * block_size + len) must not already + * be used in the sparse file. If len is not a multiple of the block size the + * data will be padded with zeros. + * + * Allows adding large amounts of data to a sparse file without needing to keep + * it all mapped. File size is limited by available virtual address space, + * exceptionally large files may need to be added in multiple chunks. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_add_file(struct sparse_file *s, + const char *filename, int64_t file_offset, unsigned int len, + unsigned int block); + +/** + * sparse_file_add_file - associate a chunk of a file with a sparse file + * + * @s - sparse file cookie + * @filename - filename of the file to be copied + * @file_offset - offset into the copied file + * @len - length of the copied block + * @block - offset in blocks into the sparse file to place the file chunk + * + * Associates a chunk of an existing fd with a sparse file cookie. + * The region [block * block_size : block * block_size + len) must not already + * be used in the sparse file. If len is not a multiple of the block size the + * data will be padded with zeros. + * + * Allows adding large amounts of data to a sparse file without needing to keep + * it all mapped. File size is limited by available virtual address space, + * exceptionally large files may need to be added in multiple chunks. + * + * The fd must remain open until the sparse file is closed or the fd block is + * removed from the sparse file. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_add_fd(struct sparse_file *s, + int fd, int64_t file_offset, unsigned int len, unsigned int block); + +/** + * sparse_file_write - write a sparse file to a file + * + * @s - sparse file cookie + * @fd - file descriptor to write to + * @gz - write a gzipped file + * @sparse - write in the Android sparse file format + * @crc - append a crc chunk + * + * Writes a sparse file to a file. If gz is true, the data will be passed + * through zlib. If sparse is true, the file will be written in the Android + * sparse file format. If sparse is false, the file will be written by seeking + * over unused chunks, producing a smaller file if the filesystem supports + * sparse files. If crc is true, the crc of the expanded data will be + * calculated and appended in a crc chunk. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, + bool crc); + +/** + * sparse_file_len - return the length of a sparse file if written to disk + * + * @s - sparse file cookie + * @sparse - write in the Android sparse file format + * @crc - append a crc chunk + * + * Returns the size a sparse file would be on disk if it were written in the + * specified format. If sparse is true, this is the size of the data in the + * sparse format. If sparse is false, this is the size of the normal + * non-sparse file. + */ +int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc); + +/** + * sparse_file_callback - call a callback for blocks in sparse file + * + * @s - sparse file cookie + * @sparse - write in the Android sparse file format + * @crc - append a crc chunk + * @write - function to call for each block + * @priv - value that will be passed as the first argument to write + * + * Writes a sparse file by calling a callback function. If sparse is true, the + * file will be written in the Android sparse file format. If crc is true, the + * crc of the expanded data will be calculated and appended in a crc chunk. + * The callback 'write' will be called with data and length for each data, + * and with data==NULL to skip over a region (only used for non-sparse format). + * The callback should return negative on error, 0 on success. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc, + int (*write)(void *priv, const void *data, int len), void *priv); + +/** + * sparse_file_read - read a file into a sparse file cookie + * + * @s - sparse file cookie + * @fd - file descriptor to read from + * @sparse - read a file in the Android sparse file format + * @crc - verify the crc of a file in the Android sparse file format + * + * Reads a file into a sparse file cookie. If sparse is true, the file is + * assumed to be in the Android sparse file format. If sparse is false, the + * file will be sparsed by looking for block aligned chunks of all zeros or + * another 32 bit value. If crc is true, the crc of the sparse file will be + * verified. + * + * Returns 0 on success, negative errno on error. + */ +int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc); + +/** + * sparse_file_import - import an existing sparse file + * + * @s - sparse file cookie + * @verbose - print verbose errors while reading the sparse file + * @crc - verify the crc of a file in the Android sparse file format + * + * Reads an existing sparse file into a sparse file cookie, recreating the same + * sparse cookie that was used to write it. If verbose is true, prints verbose + * errors when the sparse file is formatted incorrectly. + * + * Returns a new sparse file cookie on success, NULL on error. + */ +struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc); + +/** + * sparse_file_import_auto - import an existing sparse or normal file + * + * @fd - file descriptor to read from + * @crc - verify the crc of a file in the Android sparse file format + * + * Reads an existing sparse or normal file into a sparse file cookie. + * Attempts to determine if the file is sparse or not by looking for the sparse + * file magic number in the first 4 bytes. If the file is not sparse, the file + * will be sparsed by looking for block aligned chunks of all zeros or another + * 32 bit value. If crc is true, the crc of the sparse file will be verified. + * + * Returns a new sparse file cookie on success, NULL on error. + */ +struct sparse_file *sparse_file_import_auto(int fd, bool crc); + +/** sparse_file_resparse - rechunk an existing sparse file into smaller files + * + * @in_s - sparse file cookie of the existing sparse file + * @max_len - maximum file size + * @out_s - array of sparse file cookies + * @out_s_count - size of out_s array + * + * Splits chunks of an existing sparse file into smaller sparse files such that + * each sparse file is less than max_len. Returns the number of sparse_files + * that would have been written to out_s if out_s were big enough. + */ +int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len, + struct sparse_file **out_s, int out_s_count); + +/** + * sparse_file_verbose - set a sparse file cookie to print verbose errors + * + * @s - sparse file cookie + * + * Print verbose sparse file errors whenever using the sparse file cookie. + */ +void sparse_file_verbose(struct sparse_file *s); + +/** + * sparse_print_verbose - function called to print verbose errors + * + * By default, verbose errors will print to standard error. + * sparse_print_verbose may be overridden to log verbose errors somewhere else. + * + */ +extern void (*sparse_print_verbose)(const char *fmt, ...); + +#endif diff --git a/libsparse/output_file.c b/libsparse/output_file.c new file mode 100644 index 0000000..b5ae419 --- /dev/null +++ b/libsparse/output_file.c @@ -0,0 +1,772 @@ +/* + * 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. + */ + +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE 1 + +#include <fcntl.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <zlib.h> + +#include "output_file.h" +#include "sparse_format.h" +#include "sparse_crc32.h" + +#ifndef USE_MINGW +#include <sys/mman.h> +#define O_BINARY 0 +#else +#define ftruncate64 ftruncate +#endif + +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define ftruncate64 ftruncate +#define mmap64 mmap +#define off64_t off_t +#endif + +#ifdef __BIONIC__ +extern void* __mmap2(void *, size_t, int, int, int, off_t); +static inline void *mmap64(void *addr, size_t length, int prot, int flags, + int fd, off64_t offset) +{ + return __mmap2(addr, length, prot, flags, fd, offset >> 12); +} +#endif + +#define min(a, b) \ + ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) + +#define SPARSE_HEADER_MAJOR_VER 1 +#define SPARSE_HEADER_MINOR_VER 0 +#define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) +#define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) + +#define container_of(inner, outer_t, elem) \ + ((outer_t *)((char *)inner - offsetof(outer_t, elem))) + +struct output_file_ops { + int (*open)(struct output_file *, int fd); + int (*skip)(struct output_file *, int64_t); + int (*pad)(struct output_file *, int64_t); + int (*write)(struct output_file *, void *, int); + void (*close)(struct output_file *); +}; + +struct sparse_file_ops { + int (*write_data_chunk)(struct output_file *out, unsigned int len, + void *data); + int (*write_fill_chunk)(struct output_file *out, unsigned int len, + uint32_t fill_val); + int (*write_skip_chunk)(struct output_file *out, int64_t len); + int (*write_end_chunk)(struct output_file *out); +}; + +struct output_file { + int64_t cur_out_ptr; + unsigned int chunk_cnt; + uint32_t crc32; + struct output_file_ops *ops; + struct sparse_file_ops *sparse_ops; + int use_crc; + unsigned int block_size; + int64_t len; + char *zero_buf; + uint32_t *fill_buf; + char *buf; +}; + +struct output_file_gz { + struct output_file out; + gzFile gz_fd; +}; + +#define to_output_file_gz(_o) \ + container_of((_o), struct output_file_gz, out) + +struct output_file_normal { + struct output_file out; + int fd; +}; + +#define to_output_file_normal(_o) \ + container_of((_o), struct output_file_normal, out) + +struct output_file_callback { + struct output_file out; + void *priv; + int (*write)(void *priv, const void *buf, int len); +}; + +#define to_output_file_callback(_o) \ + container_of((_o), struct output_file_callback, out) + +static int file_open(struct output_file *out, int fd) +{ + struct output_file_normal *outn = to_output_file_normal(out); + + outn->fd = fd; + return 0; +} + +static int file_skip(struct output_file *out, int64_t cnt) +{ + off64_t ret; + struct output_file_normal *outn = to_output_file_normal(out); + + ret = lseek64(outn->fd, cnt, SEEK_CUR); + if (ret < 0) { + error_errno("lseek64"); + return -1; + } + return 0; +} + +static int file_pad(struct output_file *out, int64_t len) +{ + int ret; + struct output_file_normal *outn = to_output_file_normal(out); + + ret = ftruncate64(outn->fd, len); + if (ret < 0) { + return -errno; + } + + return 0; +} + +static int file_write(struct output_file *out, void *data, int len) +{ + int ret; + struct output_file_normal *outn = to_output_file_normal(out); + + ret = write(outn->fd, data, len); + if (ret < 0) { + error_errno("write"); + return -1; + } else if (ret < len) { + error("incomplete write"); + return -1; + } + + return 0; +} + +static void file_close(struct output_file *out) +{ + struct output_file_normal *outn = to_output_file_normal(out); + + free(outn); +} + +static struct output_file_ops file_ops = { + .open = file_open, + .skip = file_skip, + .pad = file_pad, + .write = file_write, + .close = file_close, +}; + +static int gz_file_open(struct output_file *out, int fd) +{ + struct output_file_gz *outgz = to_output_file_gz(out); + + outgz->gz_fd = gzdopen(fd, "wb9"); + if (!outgz->gz_fd) { + error_errno("gzopen"); + return -errno; + } + + return 0; +} + + +static int gz_file_skip(struct output_file *out, int64_t cnt) +{ + off64_t ret; + struct output_file_gz *outgz = to_output_file_gz(out); + + ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR); + if (ret < 0) { + error_errno("gzseek"); + return -1; + } + return 0; +} + +static int gz_file_pad(struct output_file *out, int64_t len) +{ + off64_t ret; + struct output_file_gz *outgz = to_output_file_gz(out); + + ret = gztell(outgz->gz_fd); + if (ret < 0) { + return -1; + } + + if (ret >= len) { + return 0; + } + + ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET); + if (ret < 0) { + return -1; + } + + gzwrite(outgz->gz_fd, "", 1); + + return 0; +} + +static int gz_file_write(struct output_file *out, void *data, int len) +{ + int ret; + struct output_file_gz *outgz = to_output_file_gz(out); + + ret = gzwrite(outgz->gz_fd, data, len); + if (ret < 0) { + error_errno("gzwrite"); + return -1; + } else if (ret < len) { + error("incomplete gzwrite"); + return -1; + } + + return 0; +} + +static void gz_file_close(struct output_file *out) +{ + struct output_file_gz *outgz = to_output_file_gz(out); + + gzclose(outgz->gz_fd); + free(outgz); +} + +static struct output_file_ops gz_file_ops = { + .open = gz_file_open, + .skip = gz_file_skip, + .pad = gz_file_pad, + .write = gz_file_write, + .close = gz_file_close, +}; + +static int callback_file_open(struct output_file *out, int fd) +{ + return 0; +} + +static int callback_file_skip(struct output_file *out, int64_t off) +{ + struct output_file_callback *outc = to_output_file_callback(out); + int to_write; + int ret; + + while (off > 0) { + to_write = min(off, (int64_t)INT_MAX); + ret = outc->write(outc->priv, NULL, to_write); + if (ret < 0) { + return ret; + } + off -= to_write; + } + + return 0; +} + +static int callback_file_pad(struct output_file *out, int64_t len) +{ + return -1; +} + +static int callback_file_write(struct output_file *out, void *data, int len) +{ + int ret; + struct output_file_callback *outc = to_output_file_callback(out); + + return outc->write(outc->priv, data, len); +} + +static void callback_file_close(struct output_file *out) +{ + struct output_file_callback *outc = to_output_file_callback(out); + + free(outc); +} + +static struct output_file_ops callback_file_ops = { + .open = callback_file_open, + .skip = callback_file_skip, + .pad = callback_file_pad, + .write = callback_file_write, + .close = callback_file_close, +}; + +int read_all(int fd, void *buf, size_t len) +{ + size_t total = 0; + int ret; + char *ptr = buf; + + while (total < len) { + ret = read(fd, ptr, len - total); + + if (ret < 0) + return -errno; + + if (ret == 0) + return -EINVAL; + + ptr += ret; + total += ret; + } + + return 0; +} + +static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len) +{ + chunk_header_t chunk_header; + int ret, chunk; + + if (skip_len % out->block_size) { + error("don't care size %llu is not a multiple of the block size %u", + skip_len, out->block_size); + return -1; + } + + /* We are skipping data, so emit a don't care chunk. */ + chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE; + chunk_header.reserved1 = 0; + chunk_header.chunk_sz = skip_len / out->block_size; + chunk_header.total_sz = CHUNK_HEADER_LEN; + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); + if (ret < 0) + return -1; + + out->cur_out_ptr += skip_len; + out->chunk_cnt++; + + return 0; +} + +static int write_sparse_fill_chunk(struct output_file *out, unsigned int len, + uint32_t fill_val) +{ + chunk_header_t chunk_header; + int rnd_up_len, zero_len, count; + int ret; + unsigned int i; + + /* Round up the fill length to a multiple of the block size */ + rnd_up_len = ALIGN(len, out->block_size); + + /* Finally we can safely emit a chunk of data */ + chunk_header.chunk_type = CHUNK_TYPE_FILL; + chunk_header.reserved1 = 0; + chunk_header.chunk_sz = rnd_up_len / out->block_size; + chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val); + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); + + if (ret < 0) + return -1; + ret = out->ops->write(out, &fill_val, sizeof(fill_val)); + if (ret < 0) + return -1; + + if (out->use_crc) { + count = out->block_size / sizeof(uint32_t); + while (count--) + out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t)); + } + + out->cur_out_ptr += rnd_up_len; + out->chunk_cnt++; + + return 0; +} + +static int write_sparse_data_chunk(struct output_file *out, unsigned int len, + void *data) +{ + chunk_header_t chunk_header; + int rnd_up_len, zero_len; + int ret; + + /* Round up the data length to a multiple of the block size */ + rnd_up_len = ALIGN(len, out->block_size); + zero_len = rnd_up_len - len; + + /* Finally we can safely emit a chunk of data */ + chunk_header.chunk_type = CHUNK_TYPE_RAW; + chunk_header.reserved1 = 0; + chunk_header.chunk_sz = rnd_up_len / out->block_size; + chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len; + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); + + if (ret < 0) + return -1; + ret = out->ops->write(out, data, len); + if (ret < 0) + return -1; + if (zero_len) { + ret = out->ops->write(out, out->zero_buf, zero_len); + if (ret < 0) + return -1; + } + + if (out->use_crc) { + out->crc32 = sparse_crc32(out->crc32, data, len); + if (zero_len) + out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len); + } + + out->cur_out_ptr += rnd_up_len; + out->chunk_cnt++; + + return 0; +} + +int write_sparse_end_chunk(struct output_file *out) +{ + chunk_header_t chunk_header; + int ret; + + if (out->use_crc) { + chunk_header.chunk_type = CHUNK_TYPE_CRC32; + chunk_header.reserved1 = 0; + chunk_header.chunk_sz = 0; + chunk_header.total_sz = CHUNK_HEADER_LEN + 4; + + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); + if (ret < 0) { + return ret; + } + out->ops->write(out, &out->crc32, 4); + if (ret < 0) { + return ret; + } + + out->chunk_cnt++; + } + + return 0; +} + +static struct sparse_file_ops sparse_file_ops = { + .write_data_chunk = write_sparse_data_chunk, + .write_fill_chunk = write_sparse_fill_chunk, + .write_skip_chunk = write_sparse_skip_chunk, + .write_end_chunk = write_sparse_end_chunk, +}; + +static int write_normal_data_chunk(struct output_file *out, unsigned int len, + void *data) +{ + int ret; + unsigned int rnd_up_len = ALIGN(len, out->block_size); + + ret = out->ops->write(out, data, len); + if (ret < 0) { + return ret; + } + + if (rnd_up_len > len) { + ret = out->ops->skip(out, rnd_up_len - len); + } + + return ret; +} + +static int write_normal_fill_chunk(struct output_file *out, unsigned int len, + uint32_t fill_val) +{ + int ret; + unsigned int i; + unsigned int write_len; + + /* Initialize fill_buf with the fill_val */ + for (i = 0; i < out->block_size / sizeof(uint32_t); i++) { + out->fill_buf[i] = fill_val; + } + + while (len) { + write_len = min(len, out->block_size); + ret = out->ops->write(out, out->fill_buf, write_len); + if (ret < 0) { + return ret; + } + + len -= write_len; + } + + return 0; +} + +static int write_normal_skip_chunk(struct output_file *out, int64_t len) +{ + return out->ops->skip(out, len); +} + +int write_normal_end_chunk(struct output_file *out) +{ + return out->ops->pad(out, out->len); +} + +static struct sparse_file_ops normal_file_ops = { + .write_data_chunk = write_normal_data_chunk, + .write_fill_chunk = write_normal_fill_chunk, + .write_skip_chunk = write_normal_skip_chunk, + .write_end_chunk = write_normal_end_chunk, +}; + +void output_file_close(struct output_file *out) +{ + int ret; + + out->sparse_ops->write_end_chunk(out); + out->ops->close(out); +} + +static int output_file_init(struct output_file *out, int block_size, + int64_t len, bool sparse, int chunks, bool crc) +{ + int ret; + + out->len = len; + out->block_size = block_size; + out->cur_out_ptr = 0ll; + out->chunk_cnt = 0; + out->crc32 = 0; + out->use_crc = crc; + + out->zero_buf = calloc(block_size, 1); + if (!out->zero_buf) { + error_errno("malloc zero_buf"); + return -ENOMEM; + } + + out->fill_buf = calloc(block_size, 1); + if (!out->fill_buf) { + error_errno("malloc fill_buf"); + ret = -ENOMEM; + goto err_fill_buf; + } + + if (sparse) { + out->sparse_ops = &sparse_file_ops; + } else { + out->sparse_ops = &normal_file_ops; + } + + if (sparse) { + sparse_header_t sparse_header = { + .magic = SPARSE_HEADER_MAGIC, + .major_version = SPARSE_HEADER_MAJOR_VER, + .minor_version = SPARSE_HEADER_MINOR_VER, + .file_hdr_sz = SPARSE_HEADER_LEN, + .chunk_hdr_sz = CHUNK_HEADER_LEN, + .blk_sz = out->block_size, + .total_blks = out->len / out->block_size, + .total_chunks = chunks, + .image_checksum = 0 + }; + + if (out->use_crc) { + sparse_header.total_chunks++; + } + + ret = out->ops->write(out, &sparse_header, sizeof(sparse_header)); + if (ret < 0) { + goto err_write; + } + } + + return 0; + +err_write: + free(out->fill_buf); +err_fill_buf: + free(out->zero_buf); + return ret; +} + +static struct output_file *output_file_new_gz(void) +{ + struct output_file_gz *outgz = calloc(1, sizeof(struct output_file_gz)); + if (!outgz) { + error_errno("malloc struct outgz"); + return NULL; + } + + outgz->out.ops = &gz_file_ops; + + return &outgz->out; +} + +static struct output_file *output_file_new_normal(void) +{ + struct output_file_normal *outn = calloc(1, sizeof(struct output_file_normal)); + if (!outn) { + error_errno("malloc struct outn"); + return NULL; + } + + outn->out.ops = &file_ops; + + return &outn->out; +} + +struct output_file *output_file_open_callback(int (*write)(void *, const void *, int), + void *priv, unsigned int block_size, int64_t len, int gz, int sparse, + int chunks, int crc) +{ + int ret; + struct output_file_callback *outc; + + outc = calloc(1, sizeof(struct output_file_callback)); + if (!outc) { + error_errno("malloc struct outc"); + return NULL; + } + + outc->out.ops = &callback_file_ops; + outc->priv = priv; + outc->write = write; + + ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc); + if (ret < 0) { + free(outc); + return NULL; + } + + return &outc->out; +} + +struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len, + int gz, int sparse, int chunks, int crc) +{ + int ret; + struct output_file *out; + + if (gz) { + out = output_file_new_gz(); + } else { + out = output_file_new_normal(); + } + + out->ops->open(out, fd); + + ret = output_file_init(out, block_size, len, sparse, chunks, crc); + if (ret < 0) { + free(out); + return NULL; + } + + return out; +} + +/* Write a contiguous region of data blocks from a memory buffer */ +int write_data_chunk(struct output_file *out, unsigned int len, void *data) +{ + return out->sparse_ops->write_data_chunk(out, len, data); +} + +/* Write a contiguous region of data blocks with a fill value */ +int write_fill_chunk(struct output_file *out, unsigned int len, + uint32_t fill_val) +{ + return out->sparse_ops->write_fill_chunk(out, len, fill_val); +} + +int write_fd_chunk(struct output_file *out, unsigned int len, + int fd, int64_t offset) +{ + int ret; + int64_t aligned_offset; + int aligned_diff; + int buffer_size; + char *ptr; + + aligned_offset = offset & ~(4096 - 1); + aligned_diff = offset - aligned_offset; + buffer_size = len + aligned_diff; + +#ifndef USE_MINGW + char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, fd, + aligned_offset); + if (data == MAP_FAILED) { + return -errno; + } + ptr = data + aligned_diff; +#else + off64_t pos; + char *data = malloc(len); + if (!data) { + return -errno; + } + pos = lseek64(fd, offset, SEEK_SET); + if (pos < 0) { + return -errno; + } + ret = read_all(fd, data, len); + if (ret < 0) { + return ret; + } + ptr = data; +#endif + + ret = out->sparse_ops->write_data_chunk(out, len, ptr); + +#ifndef USE_MINGW + munmap(data, buffer_size); +#else + free(data); +#endif + + return ret; +} + +/* Write a contiguous region of data blocks from a file */ +int write_file_chunk(struct output_file *out, unsigned int len, + const char *file, int64_t offset) +{ + int ret; + + int file_fd = open(file, O_RDONLY | O_BINARY); + if (file_fd < 0) { + return -errno; + } + + ret = write_fd_chunk(out, len, file_fd, offset); + + close(file_fd); + + return ret; +} + +int write_skip_chunk(struct output_file *out, int64_t len) +{ + return out->sparse_ops->write_skip_chunk(out, len); +} diff --git a/libsparse/output_file.h b/libsparse/output_file.h new file mode 100644 index 0000000..474c1fc --- /dev/null +++ b/libsparse/output_file.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 _OUTPUT_FILE_H_ +#define _OUTPUT_FILE_H_ + +#include <sparse/sparse.h> + +struct output_file; + +struct output_file *output_file_open_fd(int fd, unsigned int block_size, int64_t len, + int gz, int sparse, int chunks, int crc); +struct output_file *output_file_open_callback(int (*write)(void *, const void *, int), + void *priv, unsigned int block_size, int64_t len, int gz, int sparse, + int chunks, int crc); +int write_data_chunk(struct output_file *out, unsigned int len, void *data); +int write_fill_chunk(struct output_file *out, unsigned int len, + uint32_t fill_val); +int write_file_chunk(struct output_file *out, unsigned int len, + const char *file, int64_t offset); +int write_fd_chunk(struct output_file *out, unsigned int len, + int fd, int64_t offset); +int write_skip_chunk(struct output_file *out, int64_t len); +void output_file_close(struct output_file *out); + +int read_all(int fd, void *buf, size_t len); + +#endif diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c new file mode 100644 index 0000000..95e9b5b --- /dev/null +++ b/libsparse/simg2img.c @@ -0,0 +1,89 @@ +/* + * 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 <sparse/sparse.h> + +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +void usage() +{ + fprintf(stderr, "Usage: simg2img <sparse_image_files> <raw_image_file>\n"); +} + +int main(int argc, char *argv[]) +{ + int in; + int out; + int i; + int ret; + struct sparse_file *s; + + if (argc < 3) { + usage(); + exit(-1); + } + + out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664); + if (out < 0) { + fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]); + exit(-1); + } + + for (i = 1; i < argc - 1; i++) { + if (strcmp(argv[i], "-") == 0) { + in = STDIN_FILENO; + } else { + in = open(argv[i], O_RDONLY | O_BINARY); + if (in < 0) { + fprintf(stderr, "Cannot open input file %s\n", argv[i]); + exit(-1); + } + } + + s = sparse_file_import(in, true, false); + if (!s) { + fprintf(stderr, "Failed to read sparse file\n"); + exit(-1); + } + + lseek(out, SEEK_SET, 0); + + ret = sparse_file_write(s, out, false, false, false); + if (ret < 0) { + fprintf(stderr, "Cannot write output file\n"); + exit(-1); + } + sparse_file_destroy(s); + close(in); + } + + close(out); + + exit(0); +} + diff --git a/libsparse/simg2simg.c b/libsparse/simg2simg.c new file mode 100644 index 0000000..5f9ccf6 --- /dev/null +++ b/libsparse/simg2simg.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE 1 +#define _GNU_SOURCE + +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sparse/sparse.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +void usage() +{ + fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n"); +} + +int main(int argc, char *argv[]) +{ + int in; + int out; + int i; + int ret; + struct sparse_file *s; + int64_t max_size; + struct sparse_file **out_s; + int files; + char filename[4096]; + + if (argc != 4) { + usage(); + exit(-1); + } + + max_size = atoll(argv[3]); + + in = open(argv[1], O_RDONLY | O_BINARY); + if (in < 0) { + fprintf(stderr, "Cannot open input file %s\n", argv[1]); + exit(-1); + } + + s = sparse_file_import(in, true, false); + if (!s) { + fprintf(stderr, "Failed to import sparse file\n"); + exit(-1); + } + + files = sparse_file_resparse(s, max_size, NULL, 0); + if (files < 0) { + fprintf(stderr, "Failed to resparse\n"); + exit(-1); + } + + out_s = calloc(sizeof(struct sparse_file *), files); + if (!out_s) { + fprintf(stderr, "Failed to allocate sparse file array\n"); + exit(-1); + } + + files = sparse_file_resparse(s, max_size, out_s, files); + if (files < 0) { + fprintf(stderr, "Failed to resparse\n"); + exit(-1); + } + + for (i = 0; i < files; i++) { + ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i); + if (ret >= (int)sizeof(filename)) { + fprintf(stderr, "Filename too long\n"); + exit(-1); + } + + out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664); + if (out < 0) { + fprintf(stderr, "Cannot open output file %s\n", argv[2]); + exit(-1); + } + + ret = sparse_file_write(out_s[i], out, false, true, false); + if (ret) { + fprintf(stderr, "Failed to write sparse file\n"); + exit(-1); + } + close(out); + } + + close(in); + + exit(0); +} diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py new file mode 100755 index 0000000..6ece31d --- /dev/null +++ b/libsparse/simg_dump.py @@ -0,0 +1,169 @@ +#! /usr/bin/env python + +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import print_function +import getopt, posixpath, signal, struct, sys + +def usage(argv0): + print(""" +Usage: %s [-v] sparse_image_file ... + -v verbose output +""" % ( argv0 )) + sys.exit(2) + +def main(): + + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + + me = posixpath.basename(sys.argv[0]) + + # Parse the command line + verbose = 0 # -v + try: + opts, args = getopt.getopt(sys.argv[1:], + "v", + ["verbose"]) + except getopt.GetoptError, e: + print(e) + usage(me) + for o, a in opts: + if o in ("-v", "--verbose"): + verbose += 1 + else: + print("Unrecognized option \"%s\"" % (o)) + usage(me) + + if len(args) == 0: + print("No sparse_image_file specified") + usage(me) + + for path in args: + FH = open(path, 'rb') + header_bin = FH.read(28) + header = struct.unpack("<I4H4I", header_bin) + + magic = header[0] + major_version = header[1] + minor_version = header[2] + file_hdr_sz = header[3] + chunk_hdr_sz = header[4] + blk_sz = header[5] + total_blks = header[6] + total_chunks = header[7] + image_checksum = header[8] + + if magic != 0xED26FF3A: + print("%s: %s: Magic should be 0xED26FF3A but is 0x%08X" + % (me, path, magic)) + continue + if major_version != 1 or minor_version != 0: + print("%s: %s: I only know about version 1.0, but this is version %u.%u" + % (me, path, major_version, minor_version)) + continue + if file_hdr_sz != 28: + print("%s: %s: The file header size was expected to be 28, but is %u." + % (me, path, file_hdr_sz)) + continue + if chunk_hdr_sz != 12: + print("%s: %s: The chunk header size was expected to be 12, but is %u." + % (me, path, chunk_hdr_sz)) + continue + + print("%s: Total of %u %u-byte output blocks in %u input chunks." + % (path, total_blks, blk_sz, total_chunks)) + + if image_checksum != 0: + print("checksum=0x%08X" % (image_checksum)) + + if not verbose: + continue + print(" input_bytes output_blocks") + print("chunk offset number offset number") + offset = 0 + for i in xrange(1,total_chunks+1): + header_bin = FH.read(12) + header = struct.unpack("<2H2I", header_bin) + chunk_type = header[0] + reserved1 = header[1] + chunk_sz = header[2] + total_sz = header[3] + data_sz = total_sz - 12 + + print("%4u %10u %10u %7u %7u" % (i, FH.tell(), data_sz, offset, chunk_sz), + end=" ") + + if chunk_type == 0xCAC1: + if data_sz != (chunk_sz * blk_sz): + print("Raw chunk input size (%u) does not match output size (%u)" + % (data_sz, chunk_sz * blk_sz)) + break; + else: + print("Raw data", end="") + FH.read(data_sz) + elif chunk_type == 0xCAC2: + if data_sz != 4: + print("Fill chunk should have 4 bytes of fill, but this has %u" + % (data_sz), end="") + break; + else: + fill_bin = FH.read(4) + fill = struct.unpack("<I", fill_bin) + print("Fill with 0x%08X" % (fill)) + elif chunk_type == 0xCAC3: + if data_sz != 0: + print("Don't care chunk input size is non-zero (%u)" % (data_sz)) + break; + else: + print("Don't care", end="") + elif chunk_type == 0xCAC4: + if data_sz != 4: + print("CRC32 chunk should have 4 bytes of CRC, but this has %u" + % (data_sz), end="") + break; + else: + crc_bin = FH.read(4) + crc = struct.unpack("<I", crc) + print("Unverified CRC32 0x%08X" % (crc)) + else: + print("Unknown chunk type 0x%04X" % (chunk_type), end="") + break; + + if verbose > 1: + header = struct.unpack("<12B", header_bin) + print(" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)" + % (header[0], header[1], header[2], header[3], + header[4], header[5], header[6], header[7], + header[8], header[9], header[10], header[11])) + else: + print() + + offset += chunk_sz + + print(" %10u %7u End" % (FH.tell(), offset)) + + if total_blks != offset: + print("The header said we should have %u output blocks, but we saw %u" + % (total_blks, offset)) + + junk_len = len(FH.read()) + if junk_len: + print("There were %u bytes of extra data at the end of the file." + % (junk_len)) + + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/libsparse/sparse.c b/libsparse/sparse.c new file mode 100644 index 0000000..741e8c6 --- /dev/null +++ b/libsparse/sparse.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <stdlib.h> + +#include <sparse/sparse.h> + +#include "sparse_file.h" + +#include "output_file.h" +#include "backed_block.h" +#include "sparse_defs.h" +#include "sparse_format.h" + +struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len) +{ + struct sparse_file *s = calloc(sizeof(struct sparse_file), 1); + if (!s) { + return NULL; + } + + s->backed_block_list = backed_block_list_new(block_size); + if (!s->backed_block_list) { + free(s); + return NULL; + } + + s->block_size = block_size; + s->len = len; + + return s; +} + +void sparse_file_destroy(struct sparse_file *s) +{ + backed_block_list_destroy(s->backed_block_list); + free(s); +} + +int sparse_file_add_data(struct sparse_file *s, + void *data, unsigned int len, unsigned int block) +{ + return backed_block_add_data(s->backed_block_list, data, len, block); +} + +int sparse_file_add_fill(struct sparse_file *s, + uint32_t fill_val, unsigned int len, unsigned int block) +{ + return backed_block_add_fill(s->backed_block_list, fill_val, len, block); +} + +int sparse_file_add_file(struct sparse_file *s, + const char *filename, int64_t file_offset, unsigned int len, + unsigned int block) +{ + return backed_block_add_file(s->backed_block_list, filename, file_offset, + len, block); +} + +int sparse_file_add_fd(struct sparse_file *s, + int fd, int64_t file_offset, unsigned int len, unsigned int block) +{ + return backed_block_add_fd(s->backed_block_list, fd, file_offset, + len, block); +} +unsigned int sparse_count_chunks(struct sparse_file *s) +{ + struct backed_block *bb; + unsigned int last_block = 0; + unsigned int chunks = 0; + + for (bb = backed_block_iter_new(s->backed_block_list); bb; + bb = backed_block_iter_next(bb)) { + if (backed_block_block(bb) > last_block) { + /* If there is a gap between chunks, add a skip chunk */ + chunks++; + } + chunks++; + last_block = backed_block_block(bb) + + DIV_ROUND_UP(backed_block_len(bb), s->block_size); + } + if (last_block < DIV_ROUND_UP(s->len, s->block_size)) { + chunks++; + } + + return chunks; +} + +static void sparse_file_write_block(struct output_file *out, + struct backed_block *bb) +{ + switch (backed_block_type(bb)) { + case BACKED_BLOCK_DATA: + write_data_chunk(out, backed_block_len(bb), backed_block_data(bb)); + break; + case BACKED_BLOCK_FILE: + write_file_chunk(out, backed_block_len(bb), + backed_block_filename(bb), backed_block_file_offset(bb)); + break; + case BACKED_BLOCK_FD: + write_fd_chunk(out, backed_block_len(bb), + backed_block_fd(bb), backed_block_file_offset(bb)); + break; + case BACKED_BLOCK_FILL: + write_fill_chunk(out, backed_block_len(bb), + backed_block_fill_val(bb)); + break; + } +} + +static int write_all_blocks(struct sparse_file *s, struct output_file *out) +{ + struct backed_block *bb; + unsigned int last_block = 0; + int64_t pad; + + for (bb = backed_block_iter_new(s->backed_block_list); bb; + bb = backed_block_iter_next(bb)) { + if (backed_block_block(bb) > last_block) { + unsigned int blocks = backed_block_block(bb) - last_block; + write_skip_chunk(out, (int64_t)blocks * s->block_size); + } + sparse_file_write_block(out, bb); + last_block = backed_block_block(bb) + + DIV_ROUND_UP(backed_block_len(bb), s->block_size); + } + + pad = s->len - (int64_t)last_block * s->block_size; + assert(pad >= 0); + if (pad > 0) { + write_skip_chunk(out, pad); + } + + return 0; +} + +int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse, + bool crc) +{ + int ret; + int chunks; + struct output_file *out; + + chunks = sparse_count_chunks(s); + out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc); + + if (!out) + return -ENOMEM; + + ret = write_all_blocks(s, out); + + output_file_close(out); + + return ret; +} + +int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc, + int (*write)(void *priv, const void *data, int len), void *priv) +{ + int ret; + int chunks; + struct output_file *out; + + chunks = sparse_count_chunks(s); + out = output_file_open_callback(write, priv, s->block_size, s->len, false, + sparse, chunks, crc); + + if (!out) + return -ENOMEM; + + ret = write_all_blocks(s, out); + + output_file_close(out); + + return ret; +} + +static int out_counter_write(void *priv, const void *data, int len) +{ + int64_t *count = priv; + *count += len; + return 0; +} + +int64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc) +{ + int ret; + int chunks = sparse_count_chunks(s); + int64_t count = 0; + struct output_file *out; + + out = output_file_open_callback(out_counter_write, &count, + s->block_size, s->len, false, sparse, chunks, crc); + if (!out) { + return -1; + } + + ret = write_all_blocks(s, out); + + output_file_close(out); + + if (ret < 0) { + return -1; + } + + return count; +} + +static struct backed_block *move_chunks_up_to_len(struct sparse_file *from, + struct sparse_file *to, unsigned int len) +{ + int64_t count = 0; + struct output_file *out_counter; + struct backed_block *last_bb = NULL; + struct backed_block *bb; + struct backed_block *start; + int64_t file_len = 0; + + /* + * overhead is sparse file header, initial skip chunk, split chunk, end + * skip chunk, and crc chunk. + */ + int overhead = sizeof(sparse_header_t) + 4 * sizeof(chunk_header_t) + + sizeof(uint32_t); + len -= overhead; + + start = backed_block_iter_new(from->backed_block_list); + out_counter = output_file_open_callback(out_counter_write, &count, + to->block_size, to->len, false, true, 0, false); + if (!out_counter) { + return NULL; + } + + for (bb = start; bb; bb = backed_block_iter_next(bb)) { + count = 0; + /* will call out_counter_write to update count */ + sparse_file_write_block(out_counter, bb); + if (file_len + count > len) { + /* + * If the remaining available size is more than 1/8th of the + * requested size, split the chunk. Results in sparse files that + * are at least 7/8ths of the requested size + */ + if (!last_bb || (len - file_len > (len / 8))) { + backed_block_split(from->backed_block_list, bb, len - file_len); + last_bb = bb; + } + goto out; + } + file_len += count; + last_bb = bb; + } + +out: + backed_block_list_move(from->backed_block_list, + to->backed_block_list, start, last_bb); + + output_file_close(out_counter); + + return bb; +} + +int sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len, + struct sparse_file **out_s, int out_s_count) +{ + struct backed_block *bb; + unsigned int overhead; + struct sparse_file *s; + struct sparse_file *tmp; + int c = 0; + + tmp = sparse_file_new(in_s->block_size, in_s->len); + if (!tmp) { + return -ENOMEM; + } + + do { + s = sparse_file_new(in_s->block_size, in_s->len); + + bb = move_chunks_up_to_len(in_s, s, max_len); + + if (c < out_s_count) { + out_s[c] = s; + } else { + backed_block_list_move(s->backed_block_list, tmp->backed_block_list, + NULL, NULL); + sparse_file_destroy(s); + } + c++; + } while (bb); + + backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, + NULL, NULL); + + sparse_file_destroy(tmp); + + return c; +} + +void sparse_file_verbose(struct sparse_file *s) +{ + s->verbose = true; +} diff --git a/libsparse/sparse_crc32.c b/libsparse/sparse_crc32.c new file mode 100644 index 0000000..38bfe4a --- /dev/null +++ b/libsparse/sparse_crc32.c @@ -0,0 +1,111 @@ +/*- + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +/* + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + * + * + * CRC32 code derived from work by Gary S. Brown. + */ + +/* Code taken from FreeBSD 8 */ +#include <stdint.h> + +static uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +/* + * A function that calculates the CRC-32 based on the table above is + * given below for documentation purposes. An equivalent implementation + * of this function that's actually used in the kernel can be found + * in sys/libkern.h, where it can be inlined. + */ + +uint32_t sparse_crc32(uint32_t crc_in, const void *buf, int size) +{ + const uint8_t *p = buf; + uint32_t crc; + + crc = crc_in ^ ~0U; + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + return crc ^ ~0U; +} + diff --git a/nexus/NexusCommand.cpp b/libsparse/sparse_crc32.h index 541eeeb..cad8a86 100644 --- a/nexus/NexusCommand.cpp +++ b/libsparse/sparse_crc32.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -14,8 +14,7 @@ * limitations under the License. */ -#include "NexusCommand.h" +#include <stdint.h> + +uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size); -NexusCommand::NexusCommand(const char *cmd) : - FrameworkCommand(cmd) { -} diff --git a/libsparse/sparse_defs.h b/libsparse/sparse_defs.h new file mode 100644 index 0000000..b99cfd5 --- /dev/null +++ b/libsparse/sparse_defs.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 _LIBSPARSE_SPARSE_DEFS_ +#define _LIBSPARSE_SPARSE_DEFS_ + +#include <errno.h> +#include <stdio.h> + +#define __le64 u64 +#define __le32 u32 +#define __le16 u16 + +#define __be64 u64 +#define __be32 u32 +#define __be16 u16 + +#define __u64 u64 +#define __u32 u32 +#define __u16 u16 +#define __u8 u8 + +typedef unsigned long long u64; +typedef signed long long s64; +typedef unsigned int u32; +typedef unsigned short int u16; +typedef unsigned char u8; + +#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y)) +#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y))) +#define ALIGN_DOWN(x, y) ((y) * ((x) / (y))) + +#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); } while (0) +#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno)) + +#endif diff --git a/nexus/LoopController.cpp b/libsparse/sparse_err.c index f8e01d7..0f392ad 100644 --- a/nexus/LoopController.cpp +++ b/libsparse/sparse_err.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,20 @@ * limitations under the License. */ -#include <errno.h> +#include <sparse/sparse.h> -#include "LoopController.h" -#include "PropertyManager.h" +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> -LoopController::LoopController(PropertyManager *propmngr, - IControllerHandler *handlers) : - Controller("loop", propmngr, handlers) { +void sparse_default_print(const char *fmt, ...) +{ + va_list argp; + + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); } + +void (*sparse_print_error)(const char *fmt, ...) = sparse_default_print; +void (*sparse_print_verbose)(const char *fmt, ...) = sparse_default_print; diff --git a/nexus/SupplicantEventFactory.h b/libsparse/sparse_file.h index 22e5707..91a12e6 100644 --- a/nexus/SupplicantEventFactory.h +++ b/libsparse/sparse_file.h @@ -1,6 +1,5 @@ - /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,17 +14,19 @@ * limitations under the License. */ -#ifndef _SupplicantEventFactory_H -#define _SupplicantEventFactory_H +#ifndef _LIBSPARSE_SPARSE_FILE_H_ +#define _LIBSPARSE_SPARSE_FILE_H_ -class SupplicantEvent; +#include <sparse/sparse.h> -class SupplicantEventFactory { -public: - SupplicantEventFactory(); - virtual ~SupplicantEventFactory() {} +struct sparse_file { + unsigned int block_size; + int64_t len; + bool verbose; - SupplicantEvent *createEvent(char *event, size_t len); + struct backed_block_list *backed_block_list; + struct output_file *out; }; -#endif + +#endif /* _LIBSPARSE_SPARSE_FILE_H_ */ diff --git a/libsparse/sparse_format.h b/libsparse/sparse_format.h new file mode 100644 index 0000000..c41f12a --- /dev/null +++ b/libsparse/sparse_format.h @@ -0,0 +1,55 @@ +/* + * 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 _LIBSPARSE_SPARSE_FORMAT_H_ +#define _LIBSPARSE_SPARSE_FORMAT_H_ +#include "sparse_defs.h" + +typedef struct sparse_header { + __le32 magic; /* 0xed26ff3a */ + __le16 major_version; /* (0x1) - reject images with higher major versions */ + __le16 minor_version; /* (0x0) - allow images with higer minor versions */ + __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */ + __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + __le32 total_blks; /* total blocks in the non-sparse output image */ + __le32 total_chunks; /* total chunks in the sparse input image */ + __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ + /* as 0. Standard 802.3 polynomial, use a Public Domain */ + /* table implementation */ +} sparse_header_t; + +#define SPARSE_HEADER_MAGIC 0xed26ff3a + +#define CHUNK_TYPE_RAW 0xCAC1 +#define CHUNK_TYPE_FILL 0xCAC2 +#define CHUNK_TYPE_DONT_CARE 0xCAC3 +#define CHUNK_TYPE_CRC32 0xCAC4 + +typedef struct chunk_header { + __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + __le16 reserved1; + __le32 chunk_sz; /* in blocks in output image */ + __le32 total_sz; /* in bytes of chunk input file including chunk header and data */ +} chunk_header_t; + +/* Following a Raw or Fill or CRC32 chunk is data. + * For a Raw chunk, it's the data in chunk_sz * blk_sz. + * For a Fill chunk, it's 4 bytes of the fill data. + * For a CRC32 chunk, it's 4 bytes of CRC32 + */ + +#endif diff --git a/libsparse/sparse_read.c b/libsparse/sparse_read.c new file mode 100644 index 0000000..704bcfa --- /dev/null +++ b/libsparse/sparse_read.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#define _FILE_OFFSET_BITS 64 +#define _LARGEFILE64_SOURCE 1 + +#include <fcntl.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sparse/sparse.h> + +#include "sparse_crc32.h" +#include "sparse_file.h" +#include "sparse_format.h" + +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define off64_t off_t +#endif + +#define SPARSE_HEADER_MAJOR_VER 1 +#define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) +#define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) + +#define COPY_BUF_SIZE (1024U*1024U) +static char *copybuf; + +#define min(a, b) \ + ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) + +static void verbose_error(bool verbose, int err, const char *fmt, ...) +{ + char *s = ""; + char *at = ""; + if (fmt) { + va_list argp; + int size; + + va_start(argp, fmt); + size = vsnprintf(NULL, 0, fmt, argp); + va_end(argp); + + if (size < 0) { + return; + } + + at = malloc(size + 1); + if (at == NULL) { + return; + } + + va_start(argp, fmt); + vsnprintf(at, size, fmt, argp); + va_end(argp); + at[size] = 0; + s = " at "; + } + if (verbose) { +#ifndef USE_MINGW + if (err == -EOVERFLOW) { + sparse_print_verbose("EOF while reading file%s%s\n", s, at); + } else +#endif + if (err == -EINVAL) { + sparse_print_verbose("Invalid sparse file format%s%s\n", s, at); + } else if (err == -ENOMEM) { + sparse_print_verbose("Failed allocation while reading file%s%s\n", + s, at); + } else { + sparse_print_verbose("Unknown error %d%s%s\n", err, s, at); + } + } + if (fmt) { + free(at); + } +} + +static int process_raw_chunk(struct sparse_file *s, unsigned int chunk_size, + int fd, int64_t offset, unsigned int blocks, unsigned int block, + uint32_t *crc32) +{ + int ret; + int chunk; + unsigned int len = blocks * s->block_size; + + if (chunk_size % s->block_size != 0) { + return -EINVAL; + } + + if (chunk_size / s->block_size != blocks) { + return -EINVAL; + } + + ret = sparse_file_add_fd(s, fd, offset, len, block); + if (ret < 0) { + return ret; + } + + if (crc32) { + while (len) { + chunk = min(len, COPY_BUF_SIZE); + ret = read_all(fd, copybuf, chunk); + if (ret < 0) { + return ret; + } + *crc32 = sparse_crc32(*crc32, copybuf, chunk); + len -= chunk; + } + } else { + lseek64(fd, len, SEEK_CUR); + } + + return 0; +} + +static int process_fill_chunk(struct sparse_file *s, unsigned int chunk_size, + int fd, unsigned int blocks, unsigned int block, uint32_t *crc32) +{ + int ret; + int chunk; + int64_t len = (int64_t)blocks * s->block_size; + uint32_t fill_val; + uint32_t *fillbuf; + unsigned int i; + + if (chunk_size != sizeof(fill_val)) { + return -EINVAL; + } + + ret = read_all(fd, &fill_val, sizeof(fill_val)); + if (ret < 0) { + return ret; + } + + ret = sparse_file_add_fill(s, fill_val, len, block); + if (ret < 0) { + return ret; + } + + if (crc32) { + /* Fill copy_buf with the fill value */ + fillbuf = (uint32_t *)copybuf; + for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) { + fillbuf[i] = fill_val; + } + + while (len) { + chunk = min(len, COPY_BUF_SIZE); + *crc32 = sparse_crc32(*crc32, copybuf, chunk); + len -= chunk; + } + } + + return 0; +} + +static int process_skip_chunk(struct sparse_file *s, unsigned int chunk_size, + int fd, unsigned int blocks, unsigned int block, uint32_t *crc32) +{ + int ret; + int chunk; + int64_t len = (int64_t)blocks * s->block_size; + uint32_t fill_val; + uint32_t *fillbuf; + unsigned int i; + + if (chunk_size != 0) { + return -EINVAL; + } + + if (crc32) { + memset(copybuf, 0, COPY_BUF_SIZE); + + while (len) { + chunk = min(len, COPY_BUF_SIZE); + *crc32 = sparse_crc32(*crc32, copybuf, chunk); + len -= chunk; + } + } + + return 0; +} + +static int process_crc32_chunk(int fd, unsigned int chunk_size, uint32_t crc32) +{ + uint32_t file_crc32; + int ret; + + if (chunk_size != sizeof(file_crc32)) { + return -EINVAL; + } + + ret = read_all(fd, &file_crc32, sizeof(file_crc32)); + if (ret < 0) { + return ret; + } + + if (file_crc32 != crc32) { + return -EINVAL; + } + + return 0; +} + +static int process_chunk(struct sparse_file *s, int fd, off64_t offset, + unsigned int chunk_hdr_sz, chunk_header_t *chunk_header, + unsigned int cur_block, uint32_t *crc_ptr) +{ + int ret; + unsigned int chunk_data_size; + + chunk_data_size = chunk_header->total_sz - chunk_hdr_sz; + + switch (chunk_header->chunk_type) { + case CHUNK_TYPE_RAW: + ret = process_raw_chunk(s, chunk_data_size, fd, offset, + chunk_header->chunk_sz, cur_block, crc_ptr); + if (ret < 0) { + verbose_error(s->verbose, ret, "data block at %lld", offset); + return ret; + } + return chunk_header->chunk_sz; + case CHUNK_TYPE_FILL: + ret = process_fill_chunk(s, chunk_data_size, fd, + chunk_header->chunk_sz, cur_block, crc_ptr); + if (ret < 0) { + verbose_error(s->verbose, ret, "fill block at %lld", offset); + return ret; + } + return chunk_header->chunk_sz; + case CHUNK_TYPE_DONT_CARE: + ret = process_skip_chunk(s, chunk_data_size, fd, + chunk_header->chunk_sz, cur_block, crc_ptr); + if (chunk_data_size != 0) { + if (ret < 0) { + verbose_error(s->verbose, ret, "skip block at %lld", offset); + return ret; + } + } + return chunk_header->chunk_sz; + case CHUNK_TYPE_CRC32: + ret = process_crc32_chunk(fd, chunk_data_size, *crc_ptr); + if (ret < 0) { + verbose_error(s->verbose, -EINVAL, "crc block at %lld", + offset); + return ret; + } + return 0; + default: + verbose_error(s->verbose, -EINVAL, "unknown block %04X at %lld", + chunk_header->chunk_type, offset); + } + + return 0; +} + +static int sparse_file_read_sparse(struct sparse_file *s, int fd, bool crc) +{ + int ret; + unsigned int i; + sparse_header_t sparse_header; + chunk_header_t chunk_header; + uint32_t crc32 = 0; + uint32_t *crc_ptr = 0; + unsigned int cur_block = 0; + off64_t offset; + + if (!copybuf) { + copybuf = malloc(COPY_BUF_SIZE); + } + + if (!copybuf) { + return -ENOMEM; + } + + if (crc) { + crc_ptr = &crc32; + } + + ret = read_all(fd, &sparse_header, sizeof(sparse_header)); + if (ret < 0) { + return ret; + } + + if (sparse_header.magic != SPARSE_HEADER_MAGIC) { + return -EINVAL; + } + + if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) { + return -EINVAL; + } + + if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) { + return -EINVAL; + } + + if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) { + return -EINVAL; + } + + if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) { + /* Skip the remaining bytes in a header that is longer than + * we expected. + */ + lseek64(fd, sparse_header.file_hdr_sz - SPARSE_HEADER_LEN, SEEK_CUR); + } + + for (i = 0; i < sparse_header.total_chunks; i++) { + ret = read_all(fd, &chunk_header, sizeof(chunk_header)); + if (ret < 0) { + return ret; + } + + if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) { + /* Skip the remaining bytes in a header that is longer than + * we expected. + */ + lseek64(fd, sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN, SEEK_CUR); + } + + offset = lseek64(fd, 0, SEEK_CUR); + + ret = process_chunk(s, fd, offset, sparse_header.chunk_hdr_sz, &chunk_header, + cur_block, crc_ptr); + if (ret < 0) { + return ret; + } + + cur_block += ret; + } + + if (sparse_header.total_blks != cur_block) { + return -EINVAL; + } + + return 0; +} + +static int sparse_file_read_normal(struct sparse_file *s, int fd) +{ + int ret; + uint32_t *buf = malloc(s->block_size); + unsigned int block = 0; + int64_t remain = s->len; + int64_t offset = 0; + unsigned int to_read; + char *ptr; + unsigned int i; + bool sparse_block; + + if (!buf) { + return -ENOMEM; + } + + while (remain > 0) { + to_read = min(remain, s->block_size); + ret = read_all(fd, buf, to_read); + if (ret < 0) { + error("failed to read sparse file"); + return ret; + } + + if (to_read == s->block_size) { + sparse_block = true; + for (i = 1; i < s->block_size / sizeof(uint32_t); i++) { + if (buf[0] != buf[i]) { + sparse_block = false; + break; + } + } + } else { + sparse_block = false; + } + + if (sparse_block) { + /* TODO: add flag to use skip instead of fill for buf[0] == 0 */ + sparse_file_add_fill(s, buf[0], to_read, block); + } else { + sparse_file_add_fd(s, fd, offset, to_read, block); + } + + remain -= to_read; + offset += to_read; + block++; + } + + return 0; +} + +int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc) +{ + if (crc && !sparse) { + return -EINVAL; + } + + if (sparse) { + return sparse_file_read_sparse(s, fd, crc); + } else { + return sparse_file_read_normal(s, fd); + } +} + +struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc) +{ + int ret; + sparse_header_t sparse_header; + int64_t len; + struct sparse_file *s; + + ret = read_all(fd, &sparse_header, sizeof(sparse_header)); + if (ret < 0) { + verbose_error(verbose, ret, "header"); + return NULL; + } + + if (sparse_header.magic != SPARSE_HEADER_MAGIC) { + verbose_error(verbose, -EINVAL, "header magic"); + return NULL; + } + + if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) { + verbose_error(verbose, -EINVAL, "header major version"); + return NULL; + } + + if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) { + return NULL; + } + + if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) { + return NULL; + } + + len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz; + s = sparse_file_new(sparse_header.blk_sz, len); + if (!s) { + verbose_error(verbose, -EINVAL, NULL); + return NULL; + } + + ret = lseek64(fd, 0, SEEK_SET); + if (ret < 0) { + verbose_error(verbose, ret, "seeking"); + sparse_file_destroy(s); + return NULL; + } + + s->verbose = verbose; + + ret = sparse_file_read(s, fd, true, crc); + if (ret < 0) { + sparse_file_destroy(s); + return NULL; + } + + return s; +} + +struct sparse_file *sparse_file_import_auto(int fd, bool crc) +{ + struct sparse_file *s; + int64_t len; + int ret; + + s = sparse_file_import(fd, true, crc); + if (s) { + return s; + } + + len = lseek64(fd, 0, SEEK_END); + if (len < 0) { + return NULL; + } + + lseek64(fd, 0, SEEK_SET); + + s = sparse_file_new(4096, len); + if (!s) { + return NULL; + } + + ret = sparse_file_read_normal(s, fd); + if (ret < 0) { + sparse_file_destroy(s); + return NULL; + } + + return s; +} diff --git a/libsuspend/Android.mk b/libsuspend/Android.mk new file mode 100644 index 0000000..45cb701 --- /dev/null +++ b/libsuspend/Android.mk @@ -0,0 +1,23 @@ +# Copyright 2012 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) + +libsuspend_src_files := \ + autosuspend.c \ + autosuspend_autosleep.c \ + autosuspend_earlysuspend.c \ + autosuspend_wakeup_count.c \ + +libsuspend_libraries := \ + liblog libcutils + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(libsuspend_src_files) +LOCAL_MODULE := libsuspend +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include +LOCAL_SHARED_LIBRARIES := $(libsuspend_libraries) +#LOCAL_CFLAGS += -DLOG_NDEBUG=0 +include $(BUILD_SHARED_LIBRARY) diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c new file mode 100644 index 0000000..7d1d973 --- /dev/null +++ b/libsuspend/autosuspend.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdbool.h> + +#define LOG_TAG "libsuspend" +#include <cutils/log.h> + +#include <suspend/autosuspend.h> + +#include "autosuspend_ops.h" + +static struct autosuspend_ops *autosuspend_ops; +static bool autosuspend_enabled; +static bool autosuspend_inited; + +static int autosuspend_init(void) +{ + if (autosuspend_inited) { + return 0; + } + + autosuspend_inited = true; + + autosuspend_ops = autosuspend_earlysuspend_init(); + if (autosuspend_ops) { + goto out; + } + + autosuspend_ops = autosuspend_autosleep_init(); + if (autosuspend_ops) { + goto out; + } + + autosuspend_ops = autosuspend_wakeup_count_init(); + if (autosuspend_ops) { + goto out; + } + + if (!autosuspend_ops) { + ALOGE("failed to initialize autosuspend\n"); + return -1; + } + +out: + ALOGV("autosuspend initialized\n"); + return 0; +} + +int autosuspend_enable(void) +{ + int ret; + + ret = autosuspend_init(); + if (ret) { + return ret; + } + + ALOGV("autosuspend_enable\n"); + + if (autosuspend_enabled) { + return 0; + } + + ret = autosuspend_ops->enable(); + if (ret) { + return ret; + } + + autosuspend_enabled = true; + return 0; +} + +int autosuspend_disable(void) +{ + int ret; + + ret = autosuspend_init(); + if (ret) { + return ret; + } + + ALOGV("autosuspend_disable\n"); + + if (!autosuspend_enabled) { + return 0; + } + + ret = autosuspend_ops->disable(); + if (ret) { + return ret; + } + + autosuspend_enabled = false; + return 0; +} diff --git a/libsuspend/autosuspend_autosleep.c b/libsuspend/autosuspend_autosleep.c new file mode 100644 index 0000000..dd0dfef --- /dev/null +++ b/libsuspend/autosuspend_autosleep.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#define LOG_TAG "libsuspend" +#include <cutils/log.h> + +#include "autosuspend_ops.h" + +#define SYS_POWER_AUTOSLEEP "/sys/power/autosleep" + +static int autosleep_fd; +static const char *sleep_state = "mem"; +static const char *on_state = "off"; + +static int autosuspend_autosleep_enable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_autosleep_enable\n"); + + ret = write(autosleep_fd, sleep_state, strlen(sleep_state)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); + goto err; + } + + ALOGV("autosuspend_autosleep_enable done\n"); + + return 0; + +err: + return ret; +} + +static int autosuspend_autosleep_disable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_autosleep_disable\n"); + + ret = write(autosleep_fd, on_state, strlen(on_state)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_AUTOSLEEP, buf); + goto err; + } + + ALOGV("autosuspend_autosleep_disable done\n"); + + return 0; + +err: + return ret; +} + +struct autosuspend_ops autosuspend_autosleep_ops = { + .enable = autosuspend_autosleep_enable, + .disable = autosuspend_autosleep_disable, +}; + +struct autosuspend_ops *autosuspend_autosleep_init(void) +{ + int ret; + char buf[80]; + + autosleep_fd = open(SYS_POWER_AUTOSLEEP, O_WRONLY); + if (autosleep_fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", SYS_POWER_AUTOSLEEP, buf); + return NULL; + } + + ALOGI("Selected autosleep\n"); + return &autosuspend_autosleep_ops; +} diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c new file mode 100644 index 0000000..2c2aa36 --- /dev/null +++ b/libsuspend/autosuspend_earlysuspend.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#define LOG_TAG "libsuspend" +#include <cutils/log.h> + +#include "autosuspend_ops.h" + +#define EARLYSUSPEND_SYS_POWER_STATE "/sys/power/state" +#define EARLYSUSPEND_WAIT_FOR_FB_SLEEP "/sys/power/wait_for_fb_sleep" +#define EARLYSUSPEND_WAIT_FOR_FB_WAKE "/sys/power/wait_for_fb_wake" + + +static int sPowerStatefd; +static const char *pwr_state_mem = "mem"; +static const char *pwr_state_on = "on"; + +static int autosuspend_earlysuspend_enable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_earlysuspend_enable\n"); + + ret = write(sPowerStatefd, pwr_state_mem, strlen(pwr_state_mem)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err; + } + + ALOGV("autosuspend_earlysuspend_enable done\n"); + + return 0; + +err: + return ret; +} + +static int autosuspend_earlysuspend_disable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_earlysuspend_disable\n"); + + ret = write(sPowerStatefd, pwr_state_on, strlen(pwr_state_on)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err; + } + + ALOGV("autosuspend_earlysuspend_disable done\n"); + + return 0; + +err: + return ret; +} + +struct autosuspend_ops autosuspend_earlysuspend_ops = { + .enable = autosuspend_earlysuspend_enable, + .disable = autosuspend_earlysuspend_disable, +}; + +struct autosuspend_ops *autosuspend_earlysuspend_init(void) +{ + char buf[80]; + int ret; + + ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK); + if (ret < 0) { + return NULL; + } + + ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK); + if (ret < 0) { + return NULL; + } + + sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR); + + if (sPowerStatefd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + return NULL; + } + + ALOGI("Selected early suspend\n"); + return &autosuspend_earlysuspend_ops; +} diff --git a/nexus/SupplicantDisconnectedEvent.h b/libsuspend/autosuspend_ops.h index c09ec62..698e25b 100644 --- a/nexus/SupplicantDisconnectedEvent.h +++ b/libsuspend/autosuspend_ops.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,16 @@ * limitations under the License. */ -#ifndef _SupplicantDisconnectedEvent_H -#define _SupplicantDisconnectedEvent_H +#ifndef _LIBSUSPEND_AUTOSUSPEND_OPS_H_ +#define _LIBSUSPEND_AUTOSUSPEND_OPS_H_ -#include "SupplicantEvent.h" - -class SupplicantDisconnectedEvent : public SupplicantEvent { - -public: - SupplicantDisconnectedEvent(int level, char *event, size_t len); - SupplicantDisconnectedEvent(); - virtual ~SupplicantDisconnectedEvent(); +struct autosuspend_ops { + int (*enable)(void); + int (*disable)(void); }; +struct autosuspend_ops *autosuspend_autosleep_init(void); +struct autosuspend_ops *autosuspend_earlysuspend_init(void); +struct autosuspend_ops *autosuspend_wakeup_count_init(void); + #endif diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c new file mode 100644 index 0000000..b038e20 --- /dev/null +++ b/libsuspend/autosuspend_wakeup_count.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <semaphore.h> +#include <stddef.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#define LOG_TAG "libsuspend" +#include <cutils/log.h> + +#include "autosuspend_ops.h" + +#define SYS_POWER_STATE "/sys/power/state" +#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count" + +static int state_fd; +static int wakeup_count_fd; +static pthread_t suspend_thread; +static sem_t suspend_lockout; +static const char *sleep_state = "mem"; + +static void *suspend_thread_func(void *arg) +{ + char buf[80]; + char wakeup_count[20]; + int wakeup_count_len; + int ret; + + while (1) { + usleep(100000); + ALOGV("%s: read wakeup_count\n", __func__); + lseek(wakeup_count_fd, 0, SEEK_SET); + wakeup_count_len = read(wakeup_count_fd, wakeup_count, sizeof(wakeup_count)); + if (wakeup_count_len < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error reading from %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); + wakeup_count_len = 0; + continue; + } + if (!wakeup_count_len) { + ALOGE("Empty wakeup count\n"); + continue; + } + + ALOGV("%s: wait\n", __func__); + ret = sem_wait(&suspend_lockout); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error waiting on semaphore: %s\n", buf); + continue; + } + + ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count); + ret = write(wakeup_count_fd, wakeup_count, wakeup_count_len); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); + } else { + ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE); + ret = write(state_fd, sleep_state, strlen(sleep_state)); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf); + } + } + + ALOGV("%s: release sem\n", __func__); + ret = sem_post(&suspend_lockout); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error releasing semaphore: %s\n", buf); + } + } + return NULL; +} + +static int autosuspend_wakeup_count_enable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_wakeup_count_enable\n"); + + ret = sem_post(&suspend_lockout); + + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error changing semaphore: %s\n", buf); + } + + ALOGV("autosuspend_wakeup_count_enable done\n"); + + return ret; +} + +static int autosuspend_wakeup_count_disable(void) +{ + char buf[80]; + int ret; + + ALOGV("autosuspend_wakeup_count_disable\n"); + + ret = sem_wait(&suspend_lockout); + + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error changing semaphore: %s\n", buf); + } + + ALOGV("autosuspend_wakeup_count_disable done\n"); + + return ret; +} + +struct autosuspend_ops autosuspend_wakeup_count_ops = { + .enable = autosuspend_wakeup_count_enable, + .disable = autosuspend_wakeup_count_disable, +}; + +struct autosuspend_ops *autosuspend_wakeup_count_init(void) +{ + int ret; + char buf[80]; + + state_fd = open(SYS_POWER_STATE, O_RDWR); + if (state_fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", SYS_POWER_STATE, buf); + goto err_open_state; + } + + wakeup_count_fd = open(SYS_POWER_WAKEUP_COUNT, O_RDWR); + if (wakeup_count_fd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error opening %s: %s\n", SYS_POWER_WAKEUP_COUNT, buf); + goto err_open_wakeup_count; + } + + ret = sem_init(&suspend_lockout, 0, 0); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error creating semaphore: %s\n", buf); + goto err_sem_init; + } + ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL); + if (ret) { + strerror_r(ret, buf, sizeof(buf)); + ALOGE("Error creating thread: %s\n", buf); + goto err_pthread_create; + } + + ALOGI("Selected wakeup count\n"); + return &autosuspend_wakeup_count_ops; + +err_pthread_create: + sem_destroy(&suspend_lockout); +err_sem_init: + close(wakeup_count_fd); +err_open_wakeup_count: + close(state_fd); +err_open_state: + return NULL; +} diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h new file mode 100644 index 0000000..f56fc6a --- /dev/null +++ b/libsuspend/include/suspend/autosuspend.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBSUSPEND_AUTOSUSPEND_H_ +#define _LIBSUSPEND_AUTOSUSPEND_H_ + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/* + * autosuspend_enable + * + * Turn on autosuspend in the kernel, allowing it to enter suspend if no + * wakelocks/wakeup_sources are held. + * + * + * + * Returns 0 on success, -1 if autosuspend was not enabled. + */ +int autosuspend_enable(void); + +/* + * autosuspend_disable + * + * Turn off autosuspend in the kernel, preventing suspend and synchronizing + * with any in-progress resume. + * + * Returns 0 on success, -1 if autosuspend was not disabled. + */ +int autosuspend_disable(void); + +__END_DECLS + +#endif diff --git a/libsync/Android.mk b/libsync/Android.mk new file mode 100644 index 0000000..73de069 --- /dev/null +++ b/libsync/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := sync.c +LOCAL_MODULE := libsync +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES := liblog +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := sync.c sync_test.c +LOCAL_MODULE := sync_test +LOCAL_MODULE_TAGS := optional tests +LOCAL_SHARED_LIBRARIES := liblog +include $(BUILD_EXECUTABLE) diff --git a/libsync/sync.c b/libsync/sync.c new file mode 100644 index 0000000..311da14 --- /dev/null +++ b/libsync/sync.c @@ -0,0 +1,115 @@ +/* + * sync.c + * + * Copyright 2012 Google, Inc + * + * 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 <fcntl.h> +#include <stdint.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <sync/sync.h> + +int sync_wait(int fd, unsigned int timeout) +{ + __u32 to = timeout; + + return ioctl(fd, SYNC_IOC_WAIT, &to); +} + +int sync_merge(const char *name, int fd1, int fd2) +{ + struct sync_merge_data data; + int err; + + data.fd2 = fd2; + strlcpy(data.name, name, sizeof(data.name)); + + err = ioctl(fd1, SYNC_IOC_MERGE, &data); + if (err < 0) + return err; + + return data.fence; +} + +struct sync_fence_info_data *sync_fence_info(int fd) +{ + struct sync_fence_info_data *info; + int err; + + info = malloc(4096); + if (info == NULL) + return NULL; + + info->len = 4096; + err = ioctl(fd, SYNC_IOC_FENCE_INFO, info); + if (err < 0) { + free(info); + return NULL; + } + + return info; +} + +struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info, + struct sync_pt_info *itr) +{ + if (itr == NULL) + itr = (struct sync_pt_info *) info->pt_info; + else + itr = (struct sync_pt_info *) ((__u8 *)itr + itr->len); + + if ((__u8 *)itr - (__u8 *)info >= (int)info->len) + return NULL; + + return itr; +} + +void sync_fence_info_free(struct sync_fence_info_data *info) +{ + free(info); +} + + +int sw_sync_timeline_create(void) +{ + return open("/dev/sw_sync", O_RDWR); +} + +int sw_sync_timeline_inc(int fd, unsigned count) +{ + __u32 arg = count; + + return ioctl(fd, SW_SYNC_IOC_INC, &arg); +} + +int sw_sync_fence_create(int fd, const char *name, unsigned value) +{ + struct sw_sync_create_fence_data data; + int err; + + data.value = value; + strlcpy(data.name, name, sizeof(data.name)); + + err = ioctl(fd, SW_SYNC_IOC_CREATE_FENCE, &data); + if (err < 0) + return err; + + return data.fence; +} diff --git a/libsync/sync_test.c b/libsync/sync_test.c new file mode 100644 index 0000000..a75f671 --- /dev/null +++ b/libsync/sync_test.c @@ -0,0 +1,146 @@ +/* + * sync_test.c + * + * Copyright 2012 Google, Inc + * + * 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 <pthread.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <sync/sync.h> + +pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct sync_thread_data { + int thread_no; + int fd[2]; +}; + +void *sync_thread(void *data) +{ + struct sync_thread_data *sync_data = data; + struct sync_fence_info_data *info; + int err; + int i; + + for (i = 0; i < 2; i++) { + err = sync_wait(sync_data->fd[i], 10000); + + pthread_mutex_lock(&printf_mutex); + if (err < 0) { + printf("thread %d wait %d failed: %s\n", sync_data->thread_no, + i, strerror(errno)); + } else { + printf("thread %d wait %d done\n", sync_data->thread_no, i); + } + info = sync_fence_info(sync_data->fd[i]); + if (info) { + struct sync_pt_info *pt_info = NULL; + printf(" fence %s %d\n", info->name, info->status); + + while ((pt_info = sync_pt_info(info, pt_info))) { + int ts_sec = pt_info->timestamp_ns / 1000000000LL; + int ts_usec = (pt_info->timestamp_ns % 1000000000LL) / 1000LL; + printf(" pt %s %s %d %d.%06d", pt_info->obj_name, + pt_info->driver_name, pt_info->status, + ts_sec, ts_usec); + if (!strcmp(pt_info->driver_name, "sw_sync")) + printf(" val=%d\n", *(uint32_t *)pt_info->driver_data); + else + printf("\n"); + } + sync_fence_info_free(info); + } + pthread_mutex_unlock(&printf_mutex); + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + struct sync_thread_data sync_data[4]; + pthread_t threads[4]; + int sync_timeline_fd; + int i, j; + char str[256]; + + sync_timeline_fd = sw_sync_timeline_create(); + if (sync_timeline_fd < 0) { + perror("can't create sw_sync_timeline:"); + return 1; + } + + for (i = 0; i < 3; i++) { + sync_data[i].thread_no = i; + + for (j = 0; j < 2; j++) { + unsigned val = i + j * 3 + 1; + sprintf(str, "test_fence%d-%d", i, j); + int fd = sw_sync_fence_create(sync_timeline_fd, str, val); + if (fd < 0) { + printf("can't create sync pt %d: %s", val, strerror(errno)); + return 1; + } + sync_data[i].fd[j] = fd; + printf("sync_data[%d].fd[%d] = %d;\n", i, j, fd); + + } + } + + sync_data[3].thread_no = 3; + for (j = 0; j < 2; j++) { + sprintf(str, "merged_fence%d", j); + sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]); + if (sync_data[3].fd[j] < 0) { + printf("can't merge sync pts %d and %d: %s\n", + sync_data[0].fd[j], sync_data[1].fd[j], strerror(errno)); + return 1; + } + } + + for (i = 0; i < 4; i++) + pthread_create(&threads[i], NULL, sync_thread, &sync_data[i]); + + + for (i = 0; i < 3; i++) { + int err; + printf("press enter to inc to %d\n", i+1); + fgets(str, sizeof(str), stdin); + err = sw_sync_timeline_inc(sync_timeline_fd, 1); + if (err < 0) { + perror("can't increment sync obj:"); + return 1; + } + } + + printf("press enter to close sync_timeline\n"); + fgets(str, sizeof(str), stdin); + + close(sync_timeline_fd); + + printf("press enter to end test\n"); + fgets(str, sizeof(str), stdin); + + for (i = 0; i < 3; i++) { + void *val; + pthread_join(threads[i], &val); + } + + return 0; +} diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk index cccf484..57cc313 100644 --- a/libsysutils/Android.mk +++ b/libsysutils/Android.mk @@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \ src/FrameworkCommand.cpp \ src/SocketClient.cpp \ src/ServiceManager.cpp \ + EventLogTags.logtags LOCAL_MODULE:= libsysutils diff --git a/libsysutils/EventLogTags.logtags b/libsysutils/EventLogTags.logtags new file mode 100644 index 0000000..7aa5cad --- /dev/null +++ b/libsysutils/EventLogTags.logtags @@ -0,0 +1,5 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +# FrameworkListener dispatchCommand overflow +78001 dispatchCommand_overflow +65537 netlink_failure (uid|1) diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp index 3416ceb..6731cf1 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -25,9 +25,21 @@ #include <sysutils/FrameworkCommand.h> #include <sysutils/SocketClient.h> +FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) : + SocketListener(socketName, true, withSeq) { + init(socketName, withSeq); +} + FrameworkListener::FrameworkListener(const char *socketName) : - SocketListener(socketName, true) { + SocketListener(socketName, true, false) { + init(socketName, false); +} + +void FrameworkListener::init(const char *socketName, bool withSeq) { mCommands = new FrameworkCommandCollection(); + errorRate = 0; + mCommandCount = 0; + mWithSeq = withSeq; } bool FrameworkListener::onDataAvailable(SocketClient *c) { @@ -69,6 +81,7 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { bool esc = false; bool quote = false; int k; + bool haveCmdNum = !mWithSeq; memset(argv, 0, sizeof(argv)); memset(tmp, 0, sizeof(tmp)); @@ -115,9 +128,20 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { *q = *p++; if (!quote && *q == ' ') { *q = '\0'; - if (argc >= CMD_ARGS_MAX) - goto overflow; - argv[argc++] = strdup(tmp); + if (!haveCmdNum) { + char *endptr; + int cmdNum = (int)strtol(tmp, &endptr, 0); + if (endptr == NULL || *endptr != '\0') { + cli->sendMsg(500, "Invalid sequence number", false); + goto out; + } + cli->setCmdNum(cmdNum); + haveCmdNum = true; + } else { + if (argc >= CMD_ARGS_MAX) + goto overflow; + argv[argc++] = strdup(tmp); + } memset(tmp, 0, sizeof(tmp)); q = tmp; continue; @@ -140,6 +164,12 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { goto out; } + if (errorRate && (++mCommandCount % errorRate == 0)) { + /* ignore this command - let the timeout handler handle it */ + SLOGE("Faking a timeout"); + goto out; + } + for (i = mCommands->begin(); i != mCommands->end(); ++i) { FrameworkCommand *c = *i; @@ -159,6 +189,7 @@ out: return; overflow: + LOG_EVENT_INT(78001, cli->getUid()); cli->sendMsg(500, "Command too long", false); goto out; } diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp index e67b5c6..9c447ca 100644 --- a/libsysutils/src/NetlinkListener.cpp +++ b/libsysutils/src/NetlinkListener.cpp @@ -45,9 +45,13 @@ bool NetlinkListener::onDataAvailable(SocketClient *cli) { int socket = cli->getSocket(); ssize_t count; + uid_t uid = -1; - count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer))); + count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv( + socket, mBuffer, sizeof(mBuffer), &uid)); if (count < 0) { + if (uid > 0) + LOG_EVENT_INT(65537, uid); SLOGE("recvmsg failed (%s)", strerror(errno)); return false; } diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp index 69d165a..3d4984d 100644 --- a/libsysutils/src/SocketClient.cpp +++ b/libsysutils/src/SocketClient.cpp @@ -4,22 +4,32 @@ #include <sys/types.h> #include <pthread.h> #include <string.h> +#include <arpa/inet.h> #define LOG_TAG "SocketClient" #include <cutils/log.h> #include <sysutils/SocketClient.h> -SocketClient::SocketClient(int socket, bool owned) - : mSocket(socket) - , mSocketOwned(owned) - , mPid(-1) - , mUid(-1) - , mGid(-1) - , mRefCount(1) -{ +SocketClient::SocketClient(int socket, bool owned) { + init(socket, owned, false); +} + +SocketClient::SocketClient(int socket, bool owned, bool useCmdNum) { + init(socket, owned, useCmdNum); +} + +void SocketClient::init(int socket, bool owned, bool useCmdNum) { + mSocket = socket; + mSocketOwned = owned; + mUseCmdNum = useCmdNum; pthread_mutex_init(&mWriteMutex, NULL); pthread_mutex_init(&mRefCountMutex, NULL); + mPid = -1; + mUid = -1; + mGid = -1; + mRefCount = 1; + mCmdNum = 0; struct ucred creds; socklen_t szCreds = sizeof(creds); @@ -41,28 +51,85 @@ SocketClient::~SocketClient() } int SocketClient::sendMsg(int code, const char *msg, bool addErrno) { + return sendMsg(code, msg, addErrno, mUseCmdNum); +} + +int SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum) { char *buf; - const char* arg; - const char* fmt; - char tmp[1]; - int len; + int ret = 0; if (addErrno) { - fmt = "%.3d %s (%s)"; - arg = strerror(errno); + if (useCmdNum) { + ret = asprintf(&buf, "%d %d %s (%s)", code, getCmdNum(), msg, strerror(errno)); + } else { + ret = asprintf(&buf, "%d %s (%s)", code, msg, strerror(errno)); + } } else { - fmt = "%.3d %s"; - arg = NULL; + if (useCmdNum) { + ret = asprintf(&buf, "%d %d %s", code, getCmdNum(), msg); + } else { + ret = asprintf(&buf, "%d %s", code, msg); + } } - /* Measure length of required buffer */ - len = snprintf(tmp, sizeof tmp, fmt, code, msg, arg); - /* Allocate in the stack, then write to it */ - buf = (char*)alloca(len+1); - snprintf(buf, len+1, fmt, code, msg, arg); /* Send the zero-terminated message */ - return sendMsg(buf); + if (ret != -1) { + ret = sendMsg(buf); + free(buf); + } + return ret; } +/** send 3-digit code, null, binary-length, binary data */ +int SocketClient::sendBinaryMsg(int code, const void *data, int len) { + + /* 4 bytes for the code & null + 4 bytes for the len */ + char buf[8]; + /* Write the code */ + snprintf(buf, 4, "%.3d", code); + /* Write the len */ + uint32_t tmp = htonl(len); + memcpy(buf + 4, &tmp, sizeof(uint32_t)); + + pthread_mutex_lock(&mWriteMutex); + int result = sendDataLocked(buf, sizeof(buf)); + if (result == 0 && len > 0) { + result = sendDataLocked(data, len); + } + pthread_mutex_unlock(&mWriteMutex); + + return result; +} + +// Sends the code (c-string null-terminated). +int SocketClient::sendCode(int code) { + char buf[4]; + snprintf(buf, sizeof(buf), "%.3d", code); + return sendData(buf, sizeof(buf)); +} + +char *SocketClient::quoteArg(const char *arg) { + int len = strlen(arg); + char *result = (char *)malloc(len * 2 + 3); + char *current = result; + const char *end = arg + len; + + *(current++) = '"'; + while (arg < end) { + switch (*arg) { + case '\\': + case '"': + *(current++) = '\\'; // fallthrough + default: + *(current++) = *(arg++); + } + } + *(current++) = '"'; + *(current++) = '\0'; + result = (char *)realloc(result, current-result); + return result; +} + + int SocketClient::sendMsg(const char *msg) { // Send the message including null character if (sendData(msg, strlen(msg) + 1) != 0) { @@ -72,7 +139,16 @@ int SocketClient::sendMsg(const char *msg) { return 0; } -int SocketClient::sendData(const void* data, int len) { +int SocketClient::sendData(const void *data, int len) { + + pthread_mutex_lock(&mWriteMutex); + int rc = sendDataLocked(data, len); + pthread_mutex_unlock(&mWriteMutex); + + return rc; +} + +int SocketClient::sendDataLocked(const void *data, int len) { int rc = 0; const char *p = (const char*) data; int brtw = len; @@ -86,9 +162,8 @@ int SocketClient::sendData(const void* data, int len) { return 0; } - pthread_mutex_lock(&mWriteMutex); while (brtw > 0) { - rc = write(mSocket, p, brtw); + rc = send(mSocket, p, brtw, MSG_NOSIGNAL); if (rc > 0) { p += rc; brtw -= rc; @@ -98,7 +173,6 @@ int SocketClient::sendData(const void* data, int len) { if (rc < 0 && errno == EINTR) continue; - pthread_mutex_unlock(&mWriteMutex); if (rc == 0) { SLOGW("0 length write :("); errno = EIO; @@ -107,7 +181,6 @@ int SocketClient::sendData(const void* data, int len) { } return -1; } - pthread_mutex_unlock(&mWriteMutex); return 0; } diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 3f871ea..0361641 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp @@ -29,18 +29,25 @@ #include <sysutils/SocketListener.h> #include <sysutils/SocketClient.h> +#define LOG_NDEBUG 0 + SocketListener::SocketListener(const char *socketName, bool listen) { - mListen = listen; - mSocketName = socketName; - mSock = -1; - pthread_mutex_init(&mClientsLock, NULL); - mClients = new SocketClientCollection(); + init(socketName, -1, listen, false); } SocketListener::SocketListener(int socketFd, bool listen) { + init(NULL, socketFd, listen, false); +} + +SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) { + init(socketName, -1, listen, useCmdNum); +} + +void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) { mListen = listen; - mSocketName = NULL; + mSocketName = socketName; mSock = socketFd; + mUseCmdNum = useCmdNum; pthread_mutex_init(&mClientsLock, NULL); mClients = new SocketClientCollection(); } @@ -73,13 +80,14 @@ int SocketListener::startListener() { mSocketName, strerror(errno)); return -1; } + SLOGV("got mSock = %d for %s", mSock, mSocketName); } if (mListen && listen(mSock, 4) < 0) { SLOGE("Unable to listen on socket (%s)", strerror(errno)); return -1; } else if (!mListen) - mClients->push_back(new SocketClient(mSock, false)); + mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); if (pipe(mCtrlPipe)) { SLOGE("pipe failed (%s)", strerror(errno)); @@ -164,11 +172,11 @@ void SocketListener::runListener() { max = fd; } pthread_mutex_unlock(&mClientsLock); - + SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName); if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { if (errno == EINTR) continue; - SLOGE("select failed (%s)", strerror(errno)); + SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max); sleep(1); continue; } else if (!rc) @@ -184,6 +192,7 @@ void SocketListener::runListener() { do { alen = sizeof(addr); c = accept(mSock, &addr, &alen); + SLOGV("%s got %d from accept", mSocketName, c); } while (c < 0 && errno == EINTR); if (c < 0) { SLOGE("accept failed (%s)", strerror(errno)); @@ -191,7 +200,7 @@ void SocketListener::runListener() { continue; } pthread_mutex_lock(&mClientsLock); - mClients->push_back(new SocketClient(c, true)); + mClients->push_back(new SocketClient(c, true, mUseCmdNum)); pthread_mutex_unlock(&mClientsLock); } @@ -217,6 +226,7 @@ void SocketListener::runListener() { * connection-based, remove and destroy it */ if (!onDataAvailable(c) && mListen) { /* Remove the client from our array */ + SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { if (*it == c) { @@ -238,19 +248,8 @@ void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { SocketClientCollection::iterator i; for (i = mClients->begin(); i != mClients->end(); ++i) { - if ((*i)->sendMsg(code, msg, addErrno)) { - SLOGW("Error sending broadcast (%s)", strerror(errno)); - } - } - pthread_mutex_unlock(&mClientsLock); -} - -void SocketListener::sendBroadcast(const char *msg) { - pthread_mutex_lock(&mClientsLock); - SocketClientCollection::iterator i; - - for (i = mClients->begin(); i != mClients->end(); ++i) { - if ((*i)->sendMsg(msg)) { + // broadcasts are unsolicited and should not include a cmd number + if ((*i)->sendMsg(code, msg, addErrno, false)) { SLOGW("Error sending broadcast (%s)", strerror(errno)); } } diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index ae56c41..b71ce86 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -253,7 +253,7 @@ static void readLogLines(log_device_t* devices) int max = 0; int ret; int queued_lines = 0; - bool sleep = true; + bool sleep = false; int result; fd_set readset; diff --git a/nexus/Android.mk b/nexus/Android.mk deleted file mode 100644 index f9f7110..0000000 --- a/nexus/Android.mk +++ /dev/null @@ -1,66 +0,0 @@ -BUILD_NEXUS := false -ifeq ($(BUILD_NEXUS),true) - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - main.cpp \ - NexusCommand.cpp \ - CommandListener.cpp \ - Property.cpp \ - PropertyManager.cpp \ - InterfaceConfig.cpp \ - NetworkManager.cpp \ - Controller.cpp \ - WifiController.cpp \ - TiwlanWifiController.cpp \ - TiwlanEventListener.cpp \ - WifiNetwork.cpp \ - WifiStatusPoller.cpp \ - ScanResult.cpp \ - Supplicant.cpp \ - SupplicantEvent.cpp \ - SupplicantListener.cpp \ - SupplicantState.cpp \ - SupplicantEventFactory.cpp \ - SupplicantConnectedEvent.cpp \ - SupplicantAssociatingEvent.cpp \ - SupplicantAssociatedEvent.cpp \ - SupplicantStateChangeEvent.cpp \ - SupplicantScanResultsEvent.cpp \ - SupplicantConnectionTimeoutEvent.cpp \ - SupplicantDisconnectedEvent.cpp \ - SupplicantStatus.cpp \ - OpenVpnController.cpp \ - VpnController.cpp \ - LoopController.cpp \ - DhcpClient.cpp DhcpListener.cpp \ - DhcpState.cpp DhcpEvent.cpp \ - -LOCAL_MODULE:= nexus - -LOCAL_C_INCLUDES := $(KERNEL_HEADERS) -I../../../frameworks/base/include/ - -LOCAL_CFLAGS := - -LOCAL_SHARED_LIBRARIES := libsysutils libwpa_client - -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ - nexctl.c \ - -LOCAL_MODULE:= nexctl - -LOCAL_C_INCLUDES := $(KERNEL_HEADERS) - -LOCAL_CFLAGS := - -LOCAL_SHARED_LIBRARIES := libcutils - -include $(BUILD_EXECUTABLE) - -endif # ifeq ($(BUILD_NEXUS),true) diff --git a/nexus/CommandListener.cpp b/nexus/CommandListener.cpp deleted file mode 100644 index 7c934a7..0000000 --- a/nexus/CommandListener.cpp +++ /dev/null @@ -1,235 +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. - */ - -#include <stdlib.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <errno.h> - -#define LOG_TAG "CommandListener" -#include <cutils/log.h> - -#include <sysutils/SocketClient.h> - -#include "CommandListener.h" -#include "Controller.h" -#include "Property.h" -#include "NetworkManager.h" -#include "WifiController.h" -#include "VpnController.h" -#include "ResponseCode.h" - -CommandListener::CommandListener() : - FrameworkListener("nexus") { - registerCmd(new WifiScanResultsCmd()); - registerCmd(new WifiListNetworksCmd()); - registerCmd(new WifiCreateNetworkCmd()); - registerCmd(new WifiRemoveNetworkCmd()); - - registerCmd(new GetCmd()); - registerCmd(new SetCmd()); - registerCmd(new ListCmd()); -} - -/* ------------- - * Wifi Commands - * ------------ */ - -CommandListener::WifiCreateNetworkCmd::WifiCreateNetworkCmd() : - NexusCommand("wifi_create_network") { -} - -int CommandListener::WifiCreateNetworkCmd::runCommand(SocketClient *cli, - int argc, char **argv) { - NetworkManager *nm = NetworkManager::Instance(); - WifiController *wc = (WifiController *) nm->findController("WIFI"); - WifiNetwork *wn; - - if (!(wn = wc->createNetwork())) - cli->sendMsg(ResponseCode::OperationFailed, "Failed to create network", true); - else { - char tmp[128]; - sprintf(tmp, "Created network id %d.", wn->getNetworkId()); - cli->sendMsg(ResponseCode::CommandOkay, tmp, false); - } - return 0; -} - -CommandListener::WifiRemoveNetworkCmd::WifiRemoveNetworkCmd() : - NexusCommand("wifi_remove_network") { -} - -int CommandListener::WifiRemoveNetworkCmd::runCommand(SocketClient *cli, - int argc, char **argv) { - NetworkManager *nm = NetworkManager::Instance(); - WifiController *wc = (WifiController *) nm->findController("WIFI"); - - if (wc->removeNetwork(atoi(argv[1]))) - cli->sendMsg(ResponseCode::OperationFailed, "Failed to remove network", true); - else { - cli->sendMsg(ResponseCode::CommandOkay, "Network removed.", false); - } - return 0; -} - -CommandListener::WifiScanResultsCmd::WifiScanResultsCmd() : - NexusCommand("wifi_scan_results") { -} - -int CommandListener::WifiScanResultsCmd::runCommand(SocketClient *cli, - int argc, char **argv) { - NetworkManager *nm = NetworkManager::Instance(); - WifiController *wc = (WifiController *) nm->findController("WIFI"); - - ScanResultCollection *src = wc->createScanResults(); - ScanResultCollection::iterator it; - char buffer[256]; - - for(it = src->begin(); it != src->end(); ++it) { - sprintf(buffer, "%s %u %d %s %s", - (*it)->getBssid(), (*it)->getFreq(), (*it)->getLevel(), - (*it)->getFlags(), (*it)->getSsid()); - cli->sendMsg(ResponseCode::WifiScanResult, buffer, false); - delete (*it); - it = src->erase(it); - } - - delete src; - cli->sendMsg(ResponseCode::CommandOkay, "Scan results complete.", false); - return 0; -} - -CommandListener::WifiListNetworksCmd::WifiListNetworksCmd() : - NexusCommand("wifi_list_networks") { -} - -int CommandListener::WifiListNetworksCmd::runCommand(SocketClient *cli, - int argc, char **argv) { - NetworkManager *nm = NetworkManager::Instance(); - WifiController *wc = (WifiController *) nm->findController("WIFI"); - - WifiNetworkCollection *src = wc->createNetworkList(); - WifiNetworkCollection::iterator it; - char buffer[256]; - - for(it = src->begin(); it != src->end(); ++it) { - sprintf(buffer, "%d:%s", (*it)->getNetworkId(), (*it)->getSsid()); - cli->sendMsg(ResponseCode::WifiNetworkList, buffer, false); - delete (*it); - } - - delete src; - cli->sendMsg(ResponseCode::CommandOkay, "Network listing complete.", false); - return 0; -} - -/* ------------ - * Vpn Commands - * ------------ */ - -/* ---------------- - * Generic Commands - * ---------------- */ -CommandListener::GetCmd::GetCmd() : - NexusCommand("get") { -} - -int CommandListener::GetCmd::runCommand(SocketClient *cli, int argc, char **argv) { - char val[Property::ValueMaxSize]; - - if (!NetworkManager::Instance()->getPropMngr()->get(argv[1], - val, - sizeof(val))) { - goto out_inval; - } - - char *tmp; - asprintf(&tmp, "%s %s", argv[1], val); - cli->sendMsg(ResponseCode::PropertyRead, tmp, false); - free(tmp); - - cli->sendMsg(ResponseCode::CommandOkay, "Property read.", false); - return 0; -out_inval: - errno = EINVAL; - cli->sendMsg(ResponseCode::CommandParameterError, "Failed to read property.", true); - return 0; -} - -CommandListener::SetCmd::SetCmd() : - NexusCommand("set") { -} - -int CommandListener::SetCmd::runCommand(SocketClient *cli, int argc, - char **argv) { - if (NetworkManager::Instance()->getPropMngr()->set(argv[1], argv[2])) - goto out_inval; - - cli->sendMsg(ResponseCode::CommandOkay, "Property set.", false); - return 0; - -out_inval: - errno = EINVAL; - cli->sendMsg(ResponseCode::CommandParameterError, "Failed to set property.", true); - return 0; -} - -CommandListener::ListCmd::ListCmd() : - NexusCommand("list") { -} - -int CommandListener::ListCmd::runCommand(SocketClient *cli, int argc, char **argv) { - android::List<char *> *pc; - char *prefix = NULL; - - if (argc > 1) - prefix = argv[1]; - - if (!(pc = NetworkManager::Instance()->getPropMngr()->createPropertyList(prefix))) { - errno = ENODATA; - cli->sendMsg(ResponseCode::CommandParameterError, "Failed to list properties.", true); - return 0; - } - - android::List<char *>::iterator it; - - for (it = pc->begin(); it != pc->end(); ++it) { - char p_v[Property::ValueMaxSize]; - - if (!NetworkManager::Instance()->getPropMngr()->get((*it), - p_v, - sizeof(p_v))) { - ALOGW("Failed to get %s (%s)", (*it), strerror(errno)); - } - - char *buf; - if (asprintf(&buf, "%s %s", (*it), p_v) < 0) { - ALOGE("Failed to allocate memory"); - free((*it)); - continue; - } - cli->sendMsg(ResponseCode::PropertyList, buf, false); - free(buf); - - free((*it)); - } - - delete pc; - - cli->sendMsg(ResponseCode::CommandOkay, "Properties list complete.", false); - return 0; -} diff --git a/nexus/CommandListener.h b/nexus/CommandListener.h deleted file mode 100644 index 30c6dc0..0000000 --- a/nexus/CommandListener.h +++ /dev/null @@ -1,87 +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. - */ - -#ifndef _COMMANDLISTENER_H__ -#define _COMMANDLISTENER_H__ - -#include <sysutils/FrameworkListener.h> -#include "NexusCommand.h" - -class CommandListener : public FrameworkListener { -public: - CommandListener(); - virtual ~CommandListener() {} - -private: - - class WifiScanCmd : public NexusCommand { - public: - WifiScanCmd(); - virtual ~WifiScanCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; - - class WifiScanResultsCmd : public NexusCommand { - public: - WifiScanResultsCmd(); - virtual ~WifiScanResultsCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; - - class WifiCreateNetworkCmd : public NexusCommand { - public: - WifiCreateNetworkCmd(); - virtual ~WifiCreateNetworkCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; - - class WifiRemoveNetworkCmd : public NexusCommand { - public: - WifiRemoveNetworkCmd(); - virtual ~WifiRemoveNetworkCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; - - class WifiListNetworksCmd : public NexusCommand { - public: - WifiListNetworksCmd(); - virtual ~WifiListNetworksCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; - - class SetCmd : public NexusCommand { - public: - SetCmd(); - virtual ~SetCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; - - class GetCmd : public NexusCommand { - public: - GetCmd(); - virtual ~GetCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; - - class ListCmd : public NexusCommand { - public: - ListCmd(); - virtual ~ListCmd() {} - int runCommand(SocketClient *c, int argc, char ** argv); - }; -}; - -#endif diff --git a/nexus/Controller.cpp b/nexus/Controller.cpp deleted file mode 100644 index dae8783..0000000 --- a/nexus/Controller.cpp +++ /dev/null @@ -1,165 +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. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <fcntl.h> -#include <unistd.h> -#include <malloc.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#define LOG_TAG "Controller" - -#include <cutils/log.h> - -#include "Controller.h" -#include "InterfaceConfig.h" - -extern "C" int init_module(void *, unsigned int, const char *); -extern "C" int delete_module(const char *, unsigned int); - -Controller::Controller(const char *name, PropertyManager *propMngr, - IControllerHandler *handlers) { - mPropMngr = propMngr; - mName = strdup(name); - mHandlers = handlers; - mBoundInterface = NULL; -} - -Controller::~Controller() { - if (mBoundInterface) - free(mBoundInterface); - if (mName) - free(mName); -} - -int Controller::start() { - return 0; -} - -int Controller::stop() { - return 0; -} - -int Controller::loadKernelModule(char *modpath, const char *args) { - void *module; - unsigned int size; - - module = loadFile(modpath, &size); - if (!module) { - errno = -EIO; - return -1; - } - - int rc = init_module(module, size, args); - free (module); - return rc; -} - -int Controller::unloadKernelModule(const char *modtag) { - int rc = -1; - int retries = 10; - - while (retries--) { - rc = delete_module(modtag, O_NONBLOCK | O_EXCL); - if (rc < 0 && errno == EAGAIN) - usleep(1000*500); - else - break; - } - - if (rc != 0) { - ALOGW("Unable to unload kernel driver '%s' (%s)", modtag, - strerror(errno)); - } - return rc; -} - -bool Controller::isKernelModuleLoaded(const char *modtag) { - FILE *fp = fopen("/proc/modules", "r"); - - if (!fp) { - ALOGE("Unable to open /proc/modules (%s)", strerror(errno)); - return false; - } - - char line[255]; - while(fgets(line, sizeof(line), fp)) { - char *endTag = strchr(line, ' '); - - if (!endTag) { - ALOGW("Unable to find tag for line '%s'", line); - continue; - } - if (!strncmp(line, modtag, (endTag - line))) { - fclose(fp); - return true; - } - } - - fclose(fp); - return false; -} - -void *Controller::loadFile(char *filename, unsigned int *_size) -{ - int ret, fd; - struct stat sb; - ssize_t size; - void *buffer = NULL; - - /* open the file */ - fd = open(filename, O_RDONLY); - if (fd < 0) - return NULL; - - /* find out how big it is */ - if (fstat(fd, &sb) < 0) - goto bail; - size = sb.st_size; - - /* allocate memory for it to be read into */ - buffer = malloc(size); - if (!buffer) - goto bail; - - /* slurp it into our buffer */ - ret = read(fd, buffer, size); - if (ret != size) - goto bail; - - /* let the caller know how big it is */ - *_size = size; - -bail: - close(fd); - return buffer; -} - -int Controller::bindInterface(const char *ifname) { - mBoundInterface = strdup(ifname); - return 0; -} - -int Controller::unbindInterface(const char *ifname) { - free(mBoundInterface); - mBoundInterface = NULL; - return 0; -} diff --git a/nexus/Controller.h b/nexus/Controller.h deleted file mode 100644 index e7e17c5..0000000 --- a/nexus/Controller.h +++ /dev/null @@ -1,69 +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. - */ - -#ifndef _CONTROLLER_H -#define _CONTROLLER_H - -#include <unistd.h> -#include <sys/types.h> - -#include <utils/List.h> - -class PropertyManager; -class IControllerHandler; - -#include "PropertyManager.h" - -class Controller { - /* - * Name of this controller - WIFI/VPN/USBNET/BTNET/BTDUN/LOOP/etc - */ - char *mName; - - /* - * Name of the system ethernet interface which this controller is - * bound to. - */ - char *mBoundInterface; - -protected: - PropertyManager *mPropMngr; - IControllerHandler *mHandlers; - -public: - Controller(const char *name, PropertyManager *propMngr, - IControllerHandler *handlers); - virtual ~Controller(); - - virtual int start(); - virtual int stop(); - - const char *getName() { return mName; } - const char *getBoundInterface() { return mBoundInterface; } - -protected: - int loadKernelModule(char *modpath, const char *args); - bool isKernelModuleLoaded(const char *modtag); - int unloadKernelModule(const char *modtag); - int bindInterface(const char *ifname); - int unbindInterface(const char *ifname); - -private: - void *loadFile(char *filename, unsigned int *_size); -}; - -typedef android::List<Controller *> ControllerCollection; -#endif diff --git a/nexus/DhcpClient.cpp b/nexus/DhcpClient.cpp deleted file mode 100644 index 81fdf47..0000000 --- a/nexus/DhcpClient.cpp +++ /dev/null @@ -1,140 +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. - */ - -#include <stdio.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <pthread.h> - -#define LOG_TAG "DhcpClient" -#include <cutils/log.h> -#include <cutils/properties.h> - -#include <sysutils/ServiceManager.h> - -#include <netutils/ifc.h> -#include <netutils/dhcp.h> - -#include "DhcpClient.h" -#include "DhcpState.h" -#include "DhcpListener.h" -#include "IDhcpEventHandlers.h" -#include "Controller.h" - -DhcpClient::DhcpClient(IDhcpEventHandlers *handlers) : - mState(DhcpState::INIT), mHandlers(handlers) { - mServiceManager = new ServiceManager(); - mListener = NULL; - mListenerSocket = NULL; - mController = NULL; - mDoArpProbe = false; - pthread_mutex_init(&mLock, NULL); -} - -DhcpClient::~DhcpClient() { - delete mServiceManager; - if (mListener) - delete mListener; -} - -int DhcpClient::start(Controller *c) { - ALOGD("Starting DHCP service (arp probe = %d)", mDoArpProbe); - char svc[PROPERTY_VALUE_MAX]; - snprintf(svc, - sizeof(svc), - "dhcpcd:%s%s", - (!mDoArpProbe ? "-A " : ""), - c->getBoundInterface()); - - pthread_mutex_lock(&mLock); - - if (mController) { - pthread_mutex_unlock(&mLock); - errno = EBUSY; - return -1; - } - mController = c; - - sockaddr_in addr; - if ((mListenerSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - ALOGE("Failed to create DHCP listener socket"); - pthread_mutex_unlock(&mLock); - return -1; - } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - addr.sin_port = htons(DhcpClient::STATUS_MONITOR_PORT); - - if (bind(mListenerSocket, (struct sockaddr *) &addr, sizeof(addr))) { - ALOGE("Failed to bind DHCP listener socket"); - close(mListenerSocket); - mListenerSocket = -1; - pthread_mutex_unlock(&mLock); - return -1; - } - - if (mServiceManager->start(svc)) { - ALOGE("Failed to start dhcp service"); - pthread_mutex_unlock(&mLock); - return -1; - } - - mListener = new DhcpListener(mController, mListenerSocket, mHandlers); - if (mListener->startListener()) { - ALOGE("Failed to start listener"); -#if 0 - mServiceManager->stop("dhcpcd"); - return -1; -#endif - delete mListener; - mListener = NULL; - pthread_mutex_unlock(&mLock); - } - - pthread_mutex_unlock(&mLock); - return 0; -} - -int DhcpClient::stop() { - pthread_mutex_lock(&mLock); - if (!mController) { - pthread_mutex_unlock(&mLock); - return 0; - } - - if (mListener) { - mListener->stopListener(); - delete mListener; - mListener = NULL; - } - close(mListenerSocket); - - if (mServiceManager->stop("dhcpcd")) { - ALOGW("Failed to stop DHCP service (%s)", strerror(errno)); - // XXX: Kill it the hard way.. but its gotta go! - } - - mController = NULL; - pthread_mutex_unlock(&mLock); - return 0; -} - -void DhcpClient::setDoArpProbe(bool probe) { - mDoArpProbe = probe; -} diff --git a/nexus/DhcpClient.h b/nexus/DhcpClient.h deleted file mode 100644 index 42bfda0..0000000 --- a/nexus/DhcpClient.h +++ /dev/null @@ -1,54 +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. - */ - -#ifndef _DhcpClient_H -#define _DhcpClient_H - -#include <pthread.h> - -class IDhcpEventHandlers; -class ServiceManager; -class DhcpListener; -class Controller; - -class DhcpClient { -public: - static const int STATUS_MONITOR_PORT = 6666; - -private: - int mState; - IDhcpEventHandlers *mHandlers; - ServiceManager *mServiceManager; - DhcpListener *mListener; - int mListenerSocket; - pthread_mutex_t mLock; - Controller *mController; - bool mDoArpProbe; - -public: - DhcpClient(IDhcpEventHandlers *handlers); - virtual ~DhcpClient(); - - int getState() { return mState; } - bool getDoArpProbe() { return mDoArpProbe; } - void setDoArpProbe(bool probe); - - int start(Controller *c); - int stop(); -}; - -#endif diff --git a/nexus/DhcpEvent.cpp b/nexus/DhcpEvent.cpp deleted file mode 100644 index 2f1ce6f..0000000 --- a/nexus/DhcpEvent.cpp +++ /dev/null @@ -1,56 +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. - */ - -#include <stdio.h> - -#define LOG_TAG "DhcpEvent" -#include <cutils/log.h> - -#include "DhcpEvent.h" - -char *DhcpEvent::toString(int val, char *buffer, int max) { - if (val == DhcpEvent::UNKNOWN) - strncpy(buffer, "UNKNOWN", max); - else if (val == DhcpEvent::STOP) - strncpy(buffer, "STOP", max); - else if (val == DhcpEvent::RENEW) - strncpy(buffer, "RENEW", max); - else if (val == DhcpEvent::RELEASE) - strncpy(buffer, "RELEASE", max); - else if (val == DhcpEvent::TIMEOUT) - strncpy(buffer, "TIMEOUT", max); - else - strncpy(buffer, "(internal error)", max); - - return buffer; -} - -int DhcpEvent::parseString(const char *buffer) { - if (!strcasecmp(buffer, "UNKNOWN")) - return DhcpEvent::UNKNOWN; - else if (!strcasecmp(buffer, "STOP")) - return DhcpEvent::STOP; - else if (!strcasecmp(buffer, "RENEW")) - return DhcpEvent::RENEW; - else if (!strcasecmp(buffer, "RELEASE")) - return DhcpEvent::RELEASE; - else if (!strcasecmp(buffer, "TIMEOUT")) - return DhcpEvent::TIMEOUT; - else { - ALOGW("Bad event '%s'", buffer); - return -1; - } -} diff --git a/nexus/DhcpListener.cpp b/nexus/DhcpListener.cpp deleted file mode 100644 index 16c369b..0000000 --- a/nexus/DhcpListener.cpp +++ /dev/null @@ -1,108 +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. - */ - -#include <errno.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#define LOG_TAG "DhcpListener" -#include <cutils/log.h> - -#include <DhcpListener.h> -#include "IDhcpEventHandlers.h" -#include "DhcpState.h" -#include "DhcpEvent.h" -#include "Controller.h" - -DhcpListener::DhcpListener(Controller *c, int socket, IDhcpEventHandlers *handlers) : - SocketListener(socket, false) { - mHandlers = handlers; - mController = c; -} - -DhcpListener::~DhcpListener() { -} - -bool DhcpListener::onDataAvailable(SocketClient *cli) { - char buffer[255]; - int rc; - - if ((rc = read(cli->getSocket(), buffer, sizeof(buffer))) < 0) { - ALOGW("Error reading dhcp status msg (%s)", strerror(errno)); - return true; - } - - if (!strncmp(buffer, "STATE:", 6)) { - char *next = buffer; - char *tmp; - int i; - - for (i = 0; i < 2; i++) { - if (!(tmp = strsep(&next, ":"))) { - ALOGW("Error parsing state '%s'", buffer); - return true; - } - } - - int st = DhcpState::parseString(tmp); - mHandlers->onDhcpStateChanged(mController, st); - } else if (!strncmp(buffer, "ADDRINFO:", 9)) { - char *next = buffer + 9; - struct in_addr ipaddr, netmask, gateway, broadcast, dns1, dns2; - - if (!inet_aton(strsep(&next, ":"), &ipaddr)) { - ALOGW("Malformatted IP specified"); - } - if (!inet_aton(strsep(&next, ":"), &netmask)) { - ALOGW("Malformatted netmask specified"); - } - if (!inet_aton(strsep(&next, ":"), &broadcast)) { - ALOGW("Malformatted broadcast specified"); - } - if (!inet_aton(strsep(&next, ":"), &gateway)) { - ALOGW("Malformatted gateway specified"); - } - if (!inet_aton(strsep(&next, ":"), &dns1)) { - ALOGW("Malformatted dns1 specified"); - } - if (!inet_aton(strsep(&next, ":"), &dns2)) { - ALOGW("Malformatted dns2 specified"); - } - mHandlers->onDhcpLeaseUpdated(mController, &ipaddr, &netmask, - &broadcast, &gateway, &dns1, &dns2); - - } else if (!strncmp(buffer, "EVENT:", 6)) { - char *next = buffer; - char *tmp; - int i; - - for (i = 0; i < 2; i++) { - if (!(tmp = strsep(&next, ":"))) { - ALOGW("Error parsing event '%s'", buffer); - return true; - } - } - - int ev = DhcpEvent::parseString(tmp); - mHandlers->onDhcpEvent(mController, ev); - - } else { - ALOGW("Unknown DHCP monitor msg '%s'", buffer); - } - - return true; -} diff --git a/nexus/DhcpState.cpp b/nexus/DhcpState.cpp deleted file mode 100644 index b86c186..0000000 --- a/nexus/DhcpState.cpp +++ /dev/null @@ -1,80 +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. - */ - -#include <stdio.h> - -#define LOG_TAG "DhcpState" -#include <cutils/log.h> - -#include "DhcpState.h" - -char *DhcpState::toString(int val, char *buffer, int max) { - if (val == DhcpState::INIT) - strncpy(buffer, "INIT", max); - else if (val == DhcpState::DISCOVERING) - strncpy(buffer, "DISCOVERING", max); - else if (val == DhcpState::REQUESTING) - strncpy(buffer, "REQUESTING", max); - else if (val == DhcpState::BOUND) - strncpy(buffer, "BOUND", max); - else if (val == DhcpState::RENEWING) - strncpy(buffer, "RENEWING", max); - else if (val == DhcpState::REBINDING) - strncpy(buffer, "REBINDING", max); - else if (val == DhcpState::REBOOT) - strncpy(buffer, "REBOOT", max); - else if (val == DhcpState::RENEW_REQUESTED) - strncpy(buffer, "RENEW_REQUESTED", max); - else if (val == DhcpState::INIT_IPV4LL) - strncpy(buffer, "INIT_IPV4LL", max); - else if (val == DhcpState::PROBING) - strncpy(buffer, "PROBING", max); - else if (val == DhcpState::ANNOUNCING) - strncpy(buffer, "ANNOUNCING", max); - else - strncpy(buffer, "(internal error)", max); - - return buffer; -} - -int DhcpState::parseString(const char *buffer) { - if (!strcasecmp(buffer, "INIT")) - return DhcpState::INIT; - else if (!strcasecmp(buffer, "DISCOVERING")) - return DhcpState::DISCOVERING; - else if (!strcasecmp(buffer, "REQUESTING")) - return DhcpState::REQUESTING; - else if (!strcasecmp(buffer, "BOUND")) - return DhcpState::BOUND; - else if (!strcasecmp(buffer, "RENEWING")) - return DhcpState::RENEWING; - else if (!strcasecmp(buffer, "REBINDING")) - return DhcpState::REBINDING; - else if (!strcasecmp(buffer, "REBOOT")) - return DhcpState::REBOOT; - else if (!strcasecmp(buffer, "RENEW_REQUESTED")) - return DhcpState::INIT_IPV4LL; - else if (!strcasecmp(buffer, "INIT_IPV4LL")) - return DhcpState::INIT_IPV4LL; - else if (!strcasecmp(buffer, "PROBING")) - return DhcpState::PROBING; - else if (!strcasecmp(buffer, "ANNOUNCING")) - return DhcpState::ANNOUNCING; - else { - ALOGW("Bad state '%s'", buffer); - return -1; - } -} diff --git a/nexus/DhcpState.h b/nexus/DhcpState.h deleted file mode 100644 index 041c5d2..0000000 --- a/nexus/DhcpState.h +++ /dev/null @@ -1,39 +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. - */ - -#ifndef _DHCP_STATE_H -#define _DHCP_STATE_H - -class DhcpState { -public: - static const int INIT = 0; - static const int DISCOVERING = 1; - static const int REQUESTING = 2; - static const int BOUND = 3; - static const int RENEWING = 4; - static const int REBINDING = 5; - static const int REBOOT = 6; - static const int RENEW_REQUESTED = 7; - static const int INIT_IPV4LL = 8; - static const int PROBING = 9; - static const int ANNOUNCING = 10; - - static char *toString(int val, char *buffer, int max); - - static int parseString(const char *buffer); -}; - -#endif diff --git a/nexus/IControllerHandler.h b/nexus/IControllerHandler.h deleted file mode 100644 index 3151587..0000000 --- a/nexus/IControllerHandler.h +++ /dev/null @@ -1,33 +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. - */ - -#ifndef _ICONTROLLER_HANDLER_H -#define _ICONTROLLER_HANDLER_H - -class Controller; -class InterfaceConfig; - -class IControllerHandler { -public: - virtual ~IControllerHandler() {} - virtual void onInterfaceConnected(Controller *c) = 0; - virtual void onInterfaceDisconnected(Controller *c) = 0; - virtual void onControllerSuspending(Controller *c) = 0; - virtual void onControllerResumed(Controller *c) = 0; -}; - -#endif - diff --git a/nexus/IDhcpEventHandlers.h b/nexus/IDhcpEventHandlers.h deleted file mode 100644 index 2568f09..0000000 --- a/nexus/IDhcpEventHandlers.h +++ /dev/null @@ -1,33 +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. - */ - -#ifndef _IDhcpEventHandlers_H -#define _IDhcpEventHandlers_H - -class IDhcpEventHandlers { -public: - virtual ~IDhcpEventHandlers() {} - virtual void onDhcpStateChanged(Controller *c, int state) = 0; - virtual void onDhcpEvent(Controller *c, int event) = 0; - virtual void onDhcpLeaseUpdated(Controller *c, - struct in_addr *addr, struct in_addr *net, - struct in_addr *brd, - struct in_addr *gw, struct in_addr *dns1, - struct in_addr *dns2) = 0; -}; - -#endif diff --git a/nexus/ISupplicantEventHandler.h b/nexus/ISupplicantEventHandler.h deleted file mode 100644 index 1a3aa07..0000000 --- a/nexus/ISupplicantEventHandler.h +++ /dev/null @@ -1,52 +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. - */ - -#ifndef _ISUPPLICANT_EVENT_HANDLER_H -#define _ISUPPLICANT_EVENT_HANDLER_H - -class SupplicantAssociatingEvent; -class SupplicantAssociatedEvent; -class SupplicantConnectedEvent; -class SupplicantScanResultsEvent; -class SupplicantStateChangeEvent; -class SupplicantConnectionTimeoutEvent; -class SupplicantDisconnectedEvent; - -class ISupplicantEventHandler { -public: - virtual ~ISupplicantEventHandler(){} - virtual void onAssociatingEvent(SupplicantAssociatingEvent *evt) = 0; - virtual void onAssociatedEvent(SupplicantAssociatedEvent *evt) = 0; - virtual void onConnectedEvent(SupplicantConnectedEvent *evt) = 0; - virtual void onScanResultsEvent(SupplicantScanResultsEvent *evt) = 0; - virtual void onStateChangeEvent(SupplicantStateChangeEvent *evt) = 0; - virtual void onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt) = 0; - virtual void onDisconnectedEvent(SupplicantDisconnectedEvent *evt) = 0; -#if 0 - virtual void onTerminatingEvent(SupplicantEvent *evt) = 0; - virtual void onPasswordChangedEvent(SupplicantEvent *evt) = 0; - virtual void onEapNotificationEvent(SupplicantEvent *evt) = 0; - virtual void onEapStartedEvent(SupplicantEvent *evt) = 0; - virtual void onEapMethodEvent(SupplicantEvent *evt) = 0; - virtual void onEapSuccessEvent(SupplicantEvent *evt) = 0; - virtual void onEapFailureEvent(SupplicantEvent *evt) = 0; - virtual void onLinkSpeedEvent(SupplicantEvent *evt) = 0; - virtual void onDriverStateEvent(SupplicantEvent *evt) = 0; -#endif -}; - -#endif - diff --git a/nexus/InterfaceConfig.cpp b/nexus/InterfaceConfig.cpp deleted file mode 100644 index 832cbca..0000000 --- a/nexus/InterfaceConfig.cpp +++ /dev/null @@ -1,94 +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. - */ - -#include <stdlib.h> -#include <string.h> - -#define LOG_TAG "InterfaceConfig" -#include <cutils/log.h> - -#include "InterfaceConfig.h" -#include "NetworkManager.h" - -InterfaceConfig::InterfaceConfig(bool propertiesReadOnly) { - mStaticProperties.propIp = new IPV4AddressPropertyHelper("Addr", propertiesReadOnly, &mIp); - mStaticProperties.propNetmask = new IPV4AddressPropertyHelper("Netmask", propertiesReadOnly, &mNetmask); - mStaticProperties.propGateway = new IPV4AddressPropertyHelper("Gateway", propertiesReadOnly, &mGateway); - mStaticProperties.propBroadcast = new IPV4AddressPropertyHelper("Broadcast", propertiesReadOnly, &mBroadcast); - mStaticProperties.propDns = new InterfaceDnsProperty(this, propertiesReadOnly); -} - -InterfaceConfig::~InterfaceConfig() { - delete mStaticProperties.propIp; - delete mStaticProperties.propNetmask; - delete mStaticProperties.propGateway; - delete mStaticProperties.propBroadcast; - delete mStaticProperties.propDns; -} - -void InterfaceConfig::setIp(struct in_addr *addr) { - memcpy(&mIp, addr, sizeof(struct in_addr)); -} - -void InterfaceConfig::setNetmask(struct in_addr *addr) { - memcpy(&mNetmask, addr, sizeof(struct in_addr)); -} - -void InterfaceConfig::setGateway(struct in_addr *addr) { - memcpy(&mGateway, addr, sizeof(struct in_addr)); -} - -void InterfaceConfig::setBroadcast(struct in_addr *addr) { - memcpy(&mBroadcast, addr, sizeof(struct in_addr)); -} - -void InterfaceConfig::setDns(int idx, struct in_addr *addr) { - memcpy(&mDns[idx], addr, sizeof(struct in_addr)); -} - -int InterfaceConfig::attachProperties(PropertyManager *pm, const char *nsName) { - pm->attachProperty(nsName, mStaticProperties.propIp); - pm->attachProperty(nsName, mStaticProperties.propNetmask); - pm->attachProperty(nsName, mStaticProperties.propGateway); - pm->attachProperty(nsName, mStaticProperties.propBroadcast); - pm->attachProperty(nsName, mStaticProperties.propDns); - return 0; -} - -int InterfaceConfig::detachProperties(PropertyManager *pm, const char *nsName) { - pm->detachProperty(nsName, mStaticProperties.propIp); - pm->detachProperty(nsName, mStaticProperties.propNetmask); - pm->detachProperty(nsName, mStaticProperties.propGateway); - pm->detachProperty(nsName, mStaticProperties.propBroadcast); - pm->detachProperty(nsName, mStaticProperties.propDns); - return 0; -} - -InterfaceConfig::InterfaceDnsProperty::InterfaceDnsProperty(InterfaceConfig *c, - bool ro) : - IPV4AddressProperty("Dns", ro, 2) { - mCfg = c; -} - -int InterfaceConfig::InterfaceDnsProperty::set(int idx, struct in_addr *value) { - memcpy(&mCfg->mDns[idx], value, sizeof(struct in_addr)); - return 0; -} -int InterfaceConfig::InterfaceDnsProperty::get(int idx, struct in_addr *buf) { - memcpy(buf, &mCfg->mDns[idx], sizeof(struct in_addr)); - return 0; -} - diff --git a/nexus/InterfaceConfig.h b/nexus/InterfaceConfig.h deleted file mode 100644 index 3fc7390..0000000 --- a/nexus/InterfaceConfig.h +++ /dev/null @@ -1,80 +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. - */ - -#ifndef _INTERFACE_CONFIG_H -#define _INTERFACE_CONFIG_H - -#include <unistd.h> -#include <sys/types.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include "Property.h" -class PropertyManager; - -class InterfaceConfig { - class InterfaceDnsProperty; - friend class InterfaceConfig::InterfaceDnsProperty; - - struct { - IPV4AddressPropertyHelper *propIp; - IPV4AddressPropertyHelper *propNetmask; - IPV4AddressPropertyHelper *propGateway; - IPV4AddressPropertyHelper *propBroadcast; - InterfaceDnsProperty *propDns; - } mStaticProperties; - - struct in_addr mIp; - struct in_addr mNetmask; - struct in_addr mGateway; - struct in_addr mBroadcast; - struct in_addr mDns[2]; - -public: - InterfaceConfig(bool propertiesReadOnly); - virtual ~InterfaceConfig(); - - int set(const char *name, const char *value); - const char *get(const char *name, char *buffer, size_t maxsize); - - const struct in_addr &getIp() const { return mIp; } - const struct in_addr &getNetmask() const { return mNetmask; } - const struct in_addr &getGateway() const { return mGateway; } - const struct in_addr &getBroadcast() const { return mBroadcast; } - const struct in_addr &getDns(int idx) const { return mDns[idx]; } - - void setIp(struct in_addr *addr); - void setNetmask(struct in_addr *addr); - void setGateway(struct in_addr *addr); - void setBroadcast(struct in_addr *addr); - void setDns(int idx, struct in_addr *addr); - - int attachProperties(PropertyManager *pm, const char *nsName); - int detachProperties(PropertyManager *pm, const char *nsName); - -private: - - class InterfaceDnsProperty : public IPV4AddressProperty { - InterfaceConfig *mCfg; - public: - InterfaceDnsProperty(InterfaceConfig *cfg, bool ro); - int set(int idx, struct in_addr *value); - int get(int idx, struct in_addr *buffer); - }; -}; - - -#endif diff --git a/nexus/NetworkManager.cpp b/nexus/NetworkManager.cpp deleted file mode 100644 index d4285bf..0000000 --- a/nexus/NetworkManager.cpp +++ /dev/null @@ -1,205 +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. - */ - -#include <stdio.h> -#include <errno.h> - -#define LOG_TAG "Nexus" - -#include <cutils/log.h> - -#include "NetworkManager.h" -#include "InterfaceConfig.h" -#include "DhcpClient.h" -#include "DhcpState.h" -#include "DhcpEvent.h" -#include "ResponseCode.h" - -NetworkManager *NetworkManager::sInstance = NULL; - -NetworkManager *NetworkManager::Instance() { - if (!sInstance) - sInstance = new NetworkManager(new PropertyManager()); - return sInstance; -} - -NetworkManager::NetworkManager(PropertyManager *propMngr) { - mBroadcaster = NULL; - mControllerBindings = new ControllerBindingCollection(); - mPropMngr = propMngr; - mLastDhcpState = DhcpState::INIT; - mDhcp = new DhcpClient(this); -} - -NetworkManager::~NetworkManager() { -} - -int NetworkManager::run() { - if (startControllers()) { - ALOGW("Unable to start all controllers (%s)", strerror(errno)); - } - return 0; -} - -int NetworkManager::attachController(Controller *c) { - ControllerBinding *cb = new ControllerBinding(c); - mControllerBindings->push_back(cb); - return 0; -} - -int NetworkManager::startControllers() { - int rc = 0; - ControllerBindingCollection::iterator it; - - for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) { - int irc = (*it)->getController()->start(); - if (irc && !rc) - rc = irc; - } - return rc; -} - -int NetworkManager::stopControllers() { - int rc = 0; - ControllerBindingCollection::iterator it; - - for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) { - int irc = (*it)->getController()->stop(); - if (irc && !rc) - rc = irc; - } - return rc; -} - -NetworkManager::ControllerBinding *NetworkManager::lookupBinding(Controller *c) { - ControllerBindingCollection::iterator it; - - for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) { - if ((*it)->getController() == c) - return (*it); - } - errno = ENOENT; - return NULL; -} - -Controller *NetworkManager::findController(const char *name) { - ControllerBindingCollection::iterator it; - - for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) { - if (!strcasecmp((*it)->getController()->getName(), name)) - return (*it)->getController(); - } - errno = ENOENT; - return NULL; -} - -void NetworkManager::onInterfaceConnected(Controller *c) { - ALOGD("Controller %s interface %s connected", c->getName(), c->getBoundInterface()); - - if (mDhcp->start(c)) { - ALOGE("Failed to start DHCP (%s)", strerror(errno)); - return; - } -} - -void NetworkManager::onInterfaceDisconnected(Controller *c) { - ALOGD("Controller %s interface %s disconnected", c->getName(), - c->getBoundInterface()); - - mDhcp->stop(); -} - -void NetworkManager::onControllerSuspending(Controller *c) { - ALOGD("Controller %s interface %s suspending", c->getName(), - c->getBoundInterface()); - mDhcp->stop(); -} - -void NetworkManager::onControllerResumed(Controller *c) { - ALOGD("Controller %s interface %s resumed", c->getName(), - c->getBoundInterface()); -} - -void NetworkManager::onDhcpStateChanged(Controller *c, int state) { - char tmp[255]; - char tmp2[255]; - - ALOGD("onDhcpStateChanged(%s -> %s)", - DhcpState::toString(mLastDhcpState, tmp, sizeof(tmp)), - DhcpState::toString(state, tmp2, sizeof(tmp2))); - - switch(state) { - case DhcpState::BOUND: - // Refresh the 'net.xxx' for the controller - break; - case DhcpState::RENEWING: - break; - default: - break; - } - - char *tmp3; - asprintf(&tmp3, - "DHCP state changed from %d (%s) -> %d (%s)", - mLastDhcpState, - DhcpState::toString(mLastDhcpState, tmp, sizeof(tmp)), - state, - DhcpState::toString(state, tmp2, sizeof(tmp2))); - - getBroadcaster()->sendBroadcast(ResponseCode::DhcpStateChange, - tmp3, - false); - free(tmp3); - - mLastDhcpState = state; -} - -void NetworkManager::onDhcpEvent(Controller *c, int evt) { - char tmp[64]; - ALOGD("onDhcpEvent(%s)", DhcpEvent::toString(evt, tmp, sizeof(tmp))); -} - -void NetworkManager::onDhcpLeaseUpdated(Controller *c, struct in_addr *addr, - struct in_addr *net, - struct in_addr *brd, - struct in_addr *gw, - struct in_addr *dns1, - struct in_addr *dns2) { - ControllerBinding *bind = lookupBinding(c); - - if (!bind->getCurrentCfg()) - bind->setCurrentCfg(new InterfaceConfig(true)); - - bind->getCurrentCfg()->setIp(addr); - bind->getCurrentCfg()->setNetmask(net); - bind->getCurrentCfg()->setGateway(gw); - bind->getCurrentCfg()->setBroadcast(brd); - bind->getCurrentCfg()->setDns(0, dns1); - bind->getCurrentCfg()->setDns(1, dns2); -} - -NetworkManager::ControllerBinding::ControllerBinding(Controller *c) : - mController(c) { -} - -void NetworkManager::ControllerBinding::setCurrentCfg(InterfaceConfig *c) { - mCurrentCfg = c; -} - -void NetworkManager::ControllerBinding::setBoundCfg(InterfaceConfig *c) { - mBoundCfg = c; -} - diff --git a/nexus/NetworkManager.h b/nexus/NetworkManager.h deleted file mode 100644 index 93702ce..0000000 --- a/nexus/NetworkManager.h +++ /dev/null @@ -1,95 +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. - */ - -#ifndef _NETWORKMANAGER_H -#define _NETWORKMANAGER_H - -#include <utils/List.h> -#include <sysutils/SocketListener.h> - -#include "Controller.h" -#include "PropertyManager.h" -#include "IControllerHandler.h" -#include "IDhcpEventHandlers.h" - -class InterfaceConfig; -class DhcpClient; - -class NetworkManager : public IControllerHandler, public IDhcpEventHandlers { - static NetworkManager *sInstance; - - class ControllerBinding { - Controller *mController; - InterfaceConfig *mCurrentCfg; - InterfaceConfig *mBoundCfg; - - public: - ControllerBinding(Controller *c); - virtual ~ControllerBinding() {} - - InterfaceConfig *getCurrentCfg() { return mCurrentCfg; } - InterfaceConfig *getBoundCfg() { return mCurrentCfg; } - Controller *getController() { return mController; } - - void setCurrentCfg(InterfaceConfig *cfg); - void setBoundCfg(InterfaceConfig *cfg); - }; - - typedef android::List<ControllerBinding *> ControllerBindingCollection; - -private: - ControllerBindingCollection *mControllerBindings; - SocketListener *mBroadcaster; - PropertyManager *mPropMngr; - DhcpClient *mDhcp; - int mLastDhcpState; - -public: - virtual ~NetworkManager(); - - int run(); - - int attachController(Controller *controller); - - Controller *findController(const char *name); - - void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; } - SocketListener *getBroadcaster() { return mBroadcaster; } - PropertyManager *getPropMngr() { return mPropMngr; } - - static NetworkManager *Instance(); - -private: - int startControllers(); - int stopControllers(); - - NetworkManager(PropertyManager *propMngr); - ControllerBinding *lookupBinding(Controller *c); - - void onInterfaceConnected(Controller *c); - void onInterfaceDisconnected(Controller *c); - void onControllerSuspending(Controller *c); - void onControllerResumed(Controller *c); - - void onDhcpStateChanged(Controller *c, int state); - void onDhcpEvent(Controller *c, int event); - void onDhcpLeaseUpdated(Controller *c, - struct in_addr *addr, struct in_addr *net, - struct in_addr *brd, - struct in_addr *gw, struct in_addr *dns1, - struct in_addr *dns2); -}; -#endif diff --git a/nexus/OpenVpnController.cpp b/nexus/OpenVpnController.cpp deleted file mode 100644 index 6b6d892..0000000 --- a/nexus/OpenVpnController.cpp +++ /dev/null @@ -1,71 +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. - */ - -#include <errno.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#define LOG_TAG "OpenVpnController" -#include <cutils/log.h> -#include <cutils/properties.h> - -#include <sysutils/ServiceManager.h> - -#include "OpenVpnController.h" -#include "PropertyManager.h" - -#define DAEMON_PROP_NAME "vpn.openvpn.status" -#define DAEMON_CONFIG_FILE "/data/misc/openvpn/openvpn.conf" - -OpenVpnController::OpenVpnController(PropertyManager *propmngr, - IControllerHandler *handlers) : - VpnController(propmngr, handlers) { - mServiceManager = new ServiceManager(); -} - -OpenVpnController::~OpenVpnController() { - delete mServiceManager; -} - -int OpenVpnController::start() { - return VpnController::start(); -} - -int OpenVpnController::stop() { - return VpnController::stop(); -} - -int OpenVpnController::enable() { - char svc[PROPERTY_VALUE_MAX]; - char tmp[64]; - - if (!mPropMngr->get("vpn.gateway", tmp, sizeof(tmp))) { - ALOGE("Error reading property 'vpn.gateway' (%s)", strerror(errno)); - return -1; - } - snprintf(svc, sizeof(svc), "openvpn:--remote %s 1194", tmp); - - if (mServiceManager->start(svc)) - return -1; - - return 0; -} - -int OpenVpnController::disable() { - if (mServiceManager->stop("openvpn")) - return -1; - return 0; -} diff --git a/nexus/Property.cpp b/nexus/Property.cpp deleted file mode 100644 index 6cfa955..0000000 --- a/nexus/Property.cpp +++ /dev/null @@ -1,203 +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. - */ - -#include <stdlib.h> -#include <errno.h> -#include <strings.h> -#include <netinet/in.h> - -#define LOG_TAG "Property" - -#include <cutils/log.h> - -#include "Property.h" - -Property::Property(const char *name, bool readOnly, - int type, int numElements) : - mName(name), mReadOnly(readOnly), mType(type), - mNumElements(numElements) { - if (index(name, '.')) { - ALOGW("Property name %s violates namespace rules", name); - } -} - -StringProperty::StringProperty(const char *name, bool ro, int elements) : - Property(name, ro, Property::Type_STRING, elements) { -} -int StringProperty::set(int idx, int value) { - ALOGE("Integer 'set' called on string property!"); - errno = EINVAL; - return -1; -} -int StringProperty::set(int idx, struct in_addr *value) { - ALOGE("IpAddr 'set' called on string property!"); - errno = EINVAL; - return -1; -} -int StringProperty::get(int idx, int *buffer) { - ALOGE("Integer 'get' called on string property!"); - errno = EINVAL; - return -1; -} -int StringProperty::get(int idx, struct in_addr *buffer) { - ALOGE("IpAddr 'get' called on string property!"); - errno = EINVAL; - return -1; -} - -StringPropertyHelper::StringPropertyHelper(const char *name, bool ro, - char *buffer, size_t max) : - StringProperty(name, ro, 1) { - mBuffer = buffer; - mMax = max; -} - -int StringPropertyHelper::set(int idx, const char *value) { - if (idx != 0) { - ALOGW("Attempt to use array index on StringPropertyHelper::set"); - errno = EINVAL; - return -1; - } - strncpy(mBuffer, value, mMax); - return 0; -} - -int StringPropertyHelper::get(int idx, char *buffer, size_t max) { - if (idx != 0) { - ALOGW("Attempt to use array index on StringPropertyHelper::get"); - errno = EINVAL; - return -1; - } - strncpy(buffer, mBuffer, max); - return 0; -} - -IntegerProperty::IntegerProperty(const char *name, bool ro, int elements) : - Property(name, ro, Property::Type_INTEGER, elements) { -} - -int IntegerProperty::set(int idx, const char *value) { - ALOGE("String 'set' called on integer property!"); - errno = EINVAL; - return -1; -} -int IntegerProperty::set(int idx, struct in_addr *value) { - ALOGE("IpAddr 'set' called on integer property!"); - errno = EINVAL; - return -1; -} -int IntegerProperty::get(int idx, char *buffer, size_t max) { - ALOGE("String 'get' called on integer property!"); - errno = EINVAL; - return -1; -} -int IntegerProperty::get(int idx, struct in_addr *buffer) { - ALOGE("IpAddr 'get' called on integer property!"); - errno = EINVAL; - return -1; -} - -IntegerPropertyHelper::IntegerPropertyHelper(const char *name, bool ro, - int *buffer) : - IntegerProperty(name, ro, 1) { - mBuffer = buffer; -} - -int IntegerPropertyHelper::set(int idx, int value) { - if (idx != 0) { - ALOGW("Attempt to use array index on IntegerPropertyHelper::set"); - errno = EINVAL; - return -1; - } - *mBuffer = value; - return 0; -} - -int IntegerPropertyHelper::get(int idx, int *buffer) { - if (idx != 0) { - ALOGW("Attempt to use array index on IntegerPropertyHelper::get"); - errno = EINVAL; - return -1; - } - *buffer = *mBuffer; - return 0; -} - -IPV4AddressProperty::IPV4AddressProperty(const char *name, bool ro, int elements) : - Property(name, ro, Property::Type_IPV4, elements) { -} - -int IPV4AddressProperty::set(int idx, const char *value) { - ALOGE("String 'set' called on ipv4 property!"); - errno = EINVAL; - return -1; -} -int IPV4AddressProperty::set(int idx, int value) { - ALOGE("Integer 'set' called on ipv4 property!"); - errno = EINVAL; - return -1; -} -int IPV4AddressProperty::get(int idx, char *buffer, size_t max) { - ALOGE("String 'get' called on ipv4 property!"); - errno = EINVAL; - return -1; -} -int IPV4AddressProperty::get(int idx, int *buffer) { - ALOGE("Integer 'get' called on ipv4 property!"); - errno = EINVAL; - return -1; -} - -IPV4AddressPropertyHelper::IPV4AddressPropertyHelper(const char *name, bool ro, - struct in_addr *buffer) : - IPV4AddressProperty(name, ro, 1) { - mBuffer = buffer; -} - -int IPV4AddressPropertyHelper::set(int idx, struct in_addr *value) { - if (idx != 0) { - ALOGW("Attempt to use array index on IPV4AddressPropertyHelper::set"); - errno = EINVAL; - return -1; - } - memcpy(mBuffer, value, sizeof(struct in_addr)); - return 0; -} - -int IPV4AddressPropertyHelper::get(int idx, struct in_addr *buffer) { - if (idx != 0) { - ALOGW("Attempt to use array index on IPV4AddressPropertyHelper::get"); - errno = EINVAL; - return -1; - } - memcpy(buffer, mBuffer, sizeof(struct in_addr)); - return 0; -} - -PropertyNamespace::PropertyNamespace(const char *name) { - mName = strdup(name); - mProperties = new PropertyCollection(); -} - -PropertyNamespace::~PropertyNamespace() { - PropertyCollection::iterator it; - for (it = mProperties->begin(); it != mProperties->end();) { - delete (*it); - it = mProperties->erase(it); - } - delete mProperties; - free(mName); -} diff --git a/nexus/Property.h b/nexus/Property.h deleted file mode 100644 index ceea2d3..0000000 --- a/nexus/Property.h +++ /dev/null @@ -1,137 +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. - */ - -#ifndef _PROPERTY_H -#define _PROPERTY_H - -#include <netinet/in.h> -#include <utils/List.h> - -class Property { - const char *mName; - bool mReadOnly; - int mType; - int mNumElements; - -public: - static const int NameMaxSize = 128; - static const int ValueMaxSize = 255; - - static const int Type_STRING = 1; - static const int Type_INTEGER = 2; - static const int Type_IPV4 = 3; - - Property(const char *name, bool ro, int type, int elements); - virtual ~Property() {} - - virtual int set(int idx, const char *value) = 0; - virtual int set(int idx, int value) = 0; - virtual int set(int idx, struct in_addr *value) = 0; - - virtual int get(int idx, char *buffer, size_t max) = 0; - virtual int get(int idx, int *buffer) = 0; - virtual int get(int idx, struct in_addr *buffer) = 0; - - int getType() { return mType; } - bool getReadOnly() { return mReadOnly; } - int getNumElements() { return mNumElements; } - const char *getName() { return mName; } -}; - -class StringProperty : public Property { -public: - StringProperty(const char *name, bool ro, int elements); - virtual ~StringProperty() {} - - virtual int set(int idx, const char *value) = 0; - int set(int idx, int value); - int set(int idx, struct in_addr *value); - - virtual int get(int idx, char *buffer, size_t max) = 0; - int get(int idx, int *buffer); - int get(int idx, struct in_addr *buffer); -}; - -class StringPropertyHelper : public StringProperty { - char *mBuffer; - size_t mMax; -public: - StringPropertyHelper(const char *name, bool ro, - char *buffer, size_t max); - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); -}; - -class IntegerProperty : public Property { -public: - IntegerProperty(const char *name, bool ro, int elements); - virtual ~IntegerProperty() {} - - int set(int idx, const char *value); - virtual int set(int idx, int value) = 0; - int set(int idx, struct in_addr *value); - - int get(int idx, char *buffer, size_t max); - virtual int get(int idx, int *buffer) = 0; - int get(int idx, struct in_addr *buffer); -}; - -class IntegerPropertyHelper : public IntegerProperty { - int *mBuffer; -public: - IntegerPropertyHelper(const char *name, bool ro, int *buffer); - int set(int idx, int value); - int get(int idx, int *buffer); -}; - -class IPV4AddressProperty : public Property { -public: - IPV4AddressProperty(const char *name, bool ro, int elements); - virtual ~IPV4AddressProperty() {} - - int set(int idx, const char *value); - int set(int idx, int value); - virtual int set(int idx, struct in_addr *value) = 0; - - int get(int idx, char *buffer, size_t max); - int get(int idx, int *buffer); - virtual int get(int idx, struct in_addr *buffer) = 0; -}; - -class IPV4AddressPropertyHelper : public IPV4AddressProperty { - struct in_addr *mBuffer; -public: - IPV4AddressPropertyHelper(const char *name, bool ro, struct in_addr *buf); - int set(int idx, struct in_addr *value); - int get(int idx, struct in_addr *buffer); -}; - -typedef android::List<Property *> PropertyCollection; - -class PropertyNamespace { - char *mName; - PropertyCollection *mProperties; - -public: - PropertyNamespace(const char *name); - virtual ~PropertyNamespace(); - - const char *getName() { return mName; } - PropertyCollection *getProperties() { return mProperties; } -}; - -typedef android::List<PropertyNamespace *> PropertyNamespaceCollection; -#endif diff --git a/nexus/PropertyManager.cpp b/nexus/PropertyManager.cpp deleted file mode 100644 index 41cdb41..0000000 --- a/nexus/PropertyManager.cpp +++ /dev/null @@ -1,280 +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. - */ - -#include <stdlib.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#define LOG_TAG "PropertyManager" - -#include <cutils/log.h> - -#include "PropertyManager.h" - -PropertyManager::PropertyManager() { - mNamespaces = new PropertyNamespaceCollection(); - pthread_mutex_init(&mLock, NULL); -} - -PropertyManager::~PropertyManager() { - PropertyNamespaceCollection::iterator it; - - for (it = mNamespaces->begin(); it != mNamespaces->end();) { - delete (*it); - it = mNamespaces->erase(it); - } - delete mNamespaces; -} - -PropertyNamespace *PropertyManager::lookupNamespace_UNLOCKED(const char *ns) { - PropertyNamespaceCollection::iterator ns_it; - - for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) { - if (!strcasecmp(ns, (*ns_it)->getName())) - return (*ns_it); - } - errno = ENOENT; - return NULL; -} - -Property *PropertyManager::lookupProperty_UNLOCKED(PropertyNamespace *ns, const char *name) { - PropertyCollection::iterator it; - - for (it = ns->getProperties()->begin(); - it != ns->getProperties()->end(); ++it) { - if (!strcasecmp(name, (*it)->getName())) - return (*it); - } - errno = ENOENT; - return NULL; -} - -int PropertyManager::attachProperty(const char *ns_name, Property *p) { - PropertyNamespace *ns; - - ALOGD("Attaching property %s to namespace %s", p->getName(), ns_name); - pthread_mutex_lock(&mLock); - if (!(ns = lookupNamespace_UNLOCKED(ns_name))) { - ALOGD("Creating namespace %s", ns_name); - ns = new PropertyNamespace(ns_name); - mNamespaces->push_back(ns); - } - - if (lookupProperty_UNLOCKED(ns, p->getName())) { - errno = EADDRINUSE; - pthread_mutex_unlock(&mLock); - ALOGE("Failed to register property %s.%s (%s)", - ns_name, p->getName(), strerror(errno)); - return -1; - } - - ns->getProperties()->push_back(p); - pthread_mutex_unlock(&mLock); - return 0; -} - -int PropertyManager::detachProperty(const char *ns_name, Property *p) { - PropertyNamespace *ns; - - ALOGD("Detaching property %s from namespace %s", p->getName(), ns_name); - pthread_mutex_lock(&mLock); - if (!(ns = lookupNamespace_UNLOCKED(ns_name))) { - pthread_mutex_unlock(&mLock); - ALOGE("Namespace '%s' not found", ns_name); - return -1; - } - - PropertyCollection::iterator it; - - for (it = ns->getProperties()->begin(); - it != ns->getProperties()->end(); ++it) { - if (!strcasecmp(p->getName(), (*it)->getName())) { - delete ((*it)); - ns->getProperties()->erase(it); - pthread_mutex_unlock(&mLock); - return 0; - } - } - - ALOGE("Property %s.%s not found", ns_name, p->getName()); - pthread_mutex_unlock(&mLock); - errno = ENOENT; - return -1; -} - -int PropertyManager::doSet(Property *p, int idx, const char *value) { - - if (p->getReadOnly()) { - errno = EROFS; - return -1; - } - - if (p->getType() == Property::Type_STRING) { - return p->set(idx, value); - } else if (p->getType() == Property::Type_INTEGER) { - int tmp; - errno = 0; - tmp = strtol(value, (char **) NULL, 10); - if (errno) { - ALOGE("Failed to convert '%s' to int", value); - errno = EINVAL; - return -1; - } - return p->set(idx, tmp); - } else if (p->getType() == Property::Type_IPV4) { - struct in_addr tmp; - if (!inet_aton(value, &tmp)) { - ALOGE("Failed to convert '%s' to ipv4", value); - errno = EINVAL; - return -1; - } - return p->set(idx, &tmp); - } else { - ALOGE("Property '%s' has an unknown type (%d)", p->getName(), - p->getType()); - errno = EINVAL; - return -1; - } - errno = ENOENT; - return -1; -} - -int PropertyManager::doGet(Property *p, int idx, char *buffer, size_t max) { - - if (p->getType() == Property::Type_STRING) { - if (p->get(idx, buffer, max)) { - ALOGW("String property %s get failed (%s)", p->getName(), - strerror(errno)); - return -1; - } - } - else if (p->getType() == Property::Type_INTEGER) { - int tmp; - if (p->get(idx, &tmp)) { - ALOGW("Integer property %s get failed (%s)", p->getName(), - strerror(errno)); - return -1; - } - snprintf(buffer, max, "%d", tmp); - } else if (p->getType() == Property::Type_IPV4) { - struct in_addr tmp; - if (p->get(idx, &tmp)) { - ALOGW("IPV4 property %s get failed (%s)", p->getName(), - strerror(errno)); - return -1; - } - strncpy(buffer, inet_ntoa(tmp), max); - } else { - ALOGE("Property '%s' has an unknown type (%d)", p->getName(), - p->getType()); - errno = EINVAL; - return -1; - } - return 0; -} - -/* - * IPropertyManager methods - */ - -int PropertyManager::set(const char *name, const char *value) { - - ALOGD("set %s = '%s'", name, value); - pthread_mutex_lock(&mLock); - PropertyNamespaceCollection::iterator ns_it; - for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) { - PropertyCollection::iterator p_it; - for (p_it = (*ns_it)->getProperties()->begin(); - p_it != (*ns_it)->getProperties()->end(); ++p_it) { - for (int i = 0; i < (*p_it)->getNumElements(); i++) { - char fqn[255]; - char tmp[8]; - sprintf(tmp, "_%d", i); - snprintf(fqn, sizeof(fqn), "%s.%s%s", - (*ns_it)->getName(), (*p_it)->getName(), - ((*p_it)->getNumElements() > 1 ? tmp : "")); - if (!strcasecmp(name, fqn)) { - pthread_mutex_unlock(&mLock); - return doSet((*p_it), i, value); - } - } - } - } - - ALOGE("Property %s not found", name); - pthread_mutex_unlock(&mLock); - errno = ENOENT; - return -1; -} - -const char *PropertyManager::get(const char *name, char *buffer, size_t max) { - pthread_mutex_lock(&mLock); - PropertyNamespaceCollection::iterator ns_it; - for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) { - PropertyCollection::iterator p_it; - for (p_it = (*ns_it)->getProperties()->begin(); - p_it != (*ns_it)->getProperties()->end(); ++p_it) { - - for (int i = 0; i < (*p_it)->getNumElements(); i++) { - char fqn[255]; - char tmp[8]; - sprintf(tmp, "_%d", i); - snprintf(fqn, sizeof(fqn), "%s.%s%s", - (*ns_it)->getName(), (*p_it)->getName(), - ((*p_it)->getNumElements() > 1 ? tmp : "")); - if (!strcasecmp(name, fqn)) { - pthread_mutex_unlock(&mLock); - if (doGet((*p_it), i, buffer, max)) - return NULL; - return buffer; - } - } - } - } - - ALOGE("Property %s not found", name); - pthread_mutex_unlock(&mLock); - errno = ENOENT; - return NULL; -} - -android::List<char *> *PropertyManager::createPropertyList(const char *prefix) { - android::List<char *> *c = new android::List<char *>(); - - pthread_mutex_lock(&mLock); - PropertyNamespaceCollection::iterator ns_it; - for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) { - PropertyCollection::iterator p_it; - for (p_it = (*ns_it)->getProperties()->begin(); - p_it != (*ns_it)->getProperties()->end(); ++p_it) { - for (int i = 0; i < (*p_it)->getNumElements(); i++) { - char fqn[255]; - char tmp[8]; - sprintf(tmp, "_%d", i); - snprintf(fqn, sizeof(fqn), "%s.%s%s", - (*ns_it)->getName(), (*p_it)->getName(), - ((*p_it)->getNumElements() > 1 ? tmp : "")); - if (!prefix || - (prefix && !strncasecmp(fqn, prefix, strlen(prefix)))) { - c->push_back(strdup(fqn)); - } - } - } - } - pthread_mutex_unlock(&mLock); - return c; -} diff --git a/nexus/PropertyManager.h b/nexus/PropertyManager.h deleted file mode 100644 index af56a9c..0000000 --- a/nexus/PropertyManager.h +++ /dev/null @@ -1,49 +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. - */ - -#ifndef _PROPERTY_MANAGER_H -#define _PROPERTY_MANAGER_H - -#include <errno.h> -#include <pthread.h> - -#include <utils/List.h> - -#include "Property.h" - -class PropertyManager { - PropertyNamespaceCollection *mNamespaces; - pthread_mutex_t mLock; - -public: - PropertyManager(); - virtual ~PropertyManager(); - int attachProperty(const char *ns, Property *p); - int detachProperty(const char *ns, Property *p); - - android::List<char *> *createPropertyList(const char *prefix); - - int set(const char *name, const char *value); - const char *get(const char *name, char *buffer, size_t max); - -private: - PropertyNamespace *lookupNamespace_UNLOCKED(const char *ns); - Property *lookupProperty_UNLOCKED(PropertyNamespace *ns, const char *name); - int doSet(Property *p, int idx, const char *value); - int doGet(Property *p, int idx, char *buffer, size_t max); -}; - -#endif diff --git a/nexus/ResponseCode.h b/nexus/ResponseCode.h deleted file mode 100644 index 4b6cac8..0000000 --- a/nexus/ResponseCode.h +++ /dev/null @@ -1,53 +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. - */ - -#ifndef _RESPONSECODE_H -#define _RESPONSECODE_H - -class ResponseCode { -public: - // 100 series - Requestion action was initiated; expect another reply - // before proceeding with a new command. - static const int ActionInitiated = 100; - - static const int WifiScanResult = 125; - static const int WifiNetworkList = 126; - - static const int PropertyRead = 127; - static const int PropertySet = 128; - static const int PropertyList = 129; - - // 200 series - Requested action has been successfully completed - static const int CommandOkay = 200; - - // 400 series - The command was accepted but the requested action - // did not take place. - static const int OperationFailed = 400; - - // 500 series - The command was not accepted and the requested - // action did not take place. - static const int CommandSyntaxError = 500; - static const int CommandParameterError = 501; - - // 600 series - Unsolicited broadcasts - static const int UnsolicitedInformational = 600; - static const int DhcpStateChange = 605; - static const int SupplicantStateChange = 610; - static const int ScanResultsReady = 615; - static const int LinkSpeedChange = 620; - static const int RssiChange = 625; -}; -#endif diff --git a/nexus/ScanResult.cpp b/nexus/ScanResult.cpp deleted file mode 100644 index 72cb164..0000000 --- a/nexus/ScanResult.cpp +++ /dev/null @@ -1,99 +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. - */ - -#include <stdlib.h> -#include <ctype.h> - -#define LOG_TAG "ScanResult" -#include <cutils/log.h> - -#include "ScanResult.h" - -ScanResult::ScanResult() { -} - -ScanResult::ScanResult(char *rawResult) { - char *p = rawResult, *q = rawResult; - char tmp[255]; - - // BSSID - for (q = p; *q != '\t'; ++q); - strncpy(tmp, p, (q - p)); - tmp[q-p] = '\0'; - mBssid = strdup(tmp); - ++q; - - // FREQ - for (p = q; *q != '\t'; ++q); - strncpy(tmp, p, (q - p)); - tmp[q-p] = '\0'; - mFreq = atoi(tmp); - ++q; - - // LEVEL - for (p = q; *q != '\t'; ++q); - strncpy(tmp, p, (q - p)); - tmp[q-p] = '\0'; - mLevel = atoi(tmp); - ++q; - - // FLAGS - for (p = q; *q != '\t'; ++q); - strncpy(tmp, p, (q - p)); - tmp[q-p] = '\0'; - mFlags = strdup(tmp); - ++q; - - // XXX: For some reason Supplicant sometimes sends a double-tab here. - // haven't had time to dig into it ... - if (*q == '\t') - q++; - - for (p = q; *q != '\t'; ++q) { - if (*q == '\0') - break; - } - - strncpy(tmp, p, (q - p)); - tmp[q-p] = '\0'; - mSsid = strdup(tmp); - ++q; - - return; - out_bad: - ALOGW("Malformatted scan result (%s)", rawResult); -} - -ScanResult::~ScanResult() { - if (mBssid) - free(mBssid); - if (mFlags) - free(mFlags); - if (mSsid) - free(mSsid); -} - -ScanResult *ScanResult::clone() { - ScanResult *r = new ScanResult(); - - r->mBssid = strdup(mBssid); - r->mFreq = mFreq; - r->mLevel = mLevel; - r->mFlags = strdup(mFlags); - r->mSsid = strdup(mSsid); - - return r; -} diff --git a/nexus/ScanResult.h b/nexus/ScanResult.h deleted file mode 100644 index 68ad0f0..0000000 --- a/nexus/ScanResult.h +++ /dev/null @@ -1,49 +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. - */ - -#ifndef _SCAN_RESULT_H -#define _SCAN_RESULT_H - -#include <sys/types.h> - -#include <utils/List.h> - -class ScanResult { - char *mBssid; - uint32_t mFreq; - int mLevel; - char *mFlags; - char *mSsid; - -private: - ScanResult(); - -public: - ScanResult(char *rawResult); - virtual ~ScanResult(); - - ScanResult *clone(); - - const char *getBssid() { return mBssid; } - uint32_t getFreq() { return mFreq; } - int getLevel() { return mLevel; } - const char *getFlags() { return mFlags; } - const char *getSsid() { return mSsid; } -}; - -typedef android::List<ScanResult *> ScanResultCollection; - -#endif diff --git a/nexus/Supplicant.cpp b/nexus/Supplicant.cpp deleted file mode 100644 index b604698..0000000 --- a/nexus/Supplicant.cpp +++ /dev/null @@ -1,677 +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. - */ - -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <errno.h> - -#define LOG_TAG "Supplicant" -#include <cutils/log.h> -#include <cutils/properties.h> - -#include "private/android_filesystem_config.h" - -#include <sysutils/ServiceManager.h> - -#include "Supplicant.h" -#include "SupplicantListener.h" -#include "NetworkManager.h" -#include "WifiController.h" -#include "SupplicantStatus.h" - -#include "libwpa_client/wpa_ctrl.h" - -#define IFACE_DIR "/data/system/wpa_supplicant" -#define DRIVER_PROP_NAME "wlan.driver.status" -#define SUPPLICANT_SERVICE_NAME "wpa_supplicant" -#define SUPP_CONFIG_TEMPLATE "/system/etc/wifi/wpa_supplicant.conf" -#define SUPP_CONFIG_FILE "/data/misc/wifi/wpa_supplicant.conf" - -Supplicant::Supplicant(WifiController *wc, ISupplicantEventHandler *handlers) { - mHandlers = handlers; - mController = wc; - mInterfaceName = NULL; - mCtrl = NULL; - mMonitor = NULL; - mListener = NULL; - - mServiceManager = new ServiceManager(); - - mNetworks = new WifiNetworkCollection(); - pthread_mutex_init(&mNetworksLock, NULL); -} - -Supplicant::~Supplicant() { - delete mServiceManager; - if (mInterfaceName) - free(mInterfaceName); -} - -int Supplicant::start() { - - if (setupConfig()) { - ALOGW("Unable to setup supplicant.conf"); - } - - if (mServiceManager->start(SUPPLICANT_SERVICE_NAME)) { - ALOGE("Error starting supplicant (%s)", strerror(errno)); - return -1; - } - - wpa_ctrl_cleanup(); - if (connectToSupplicant()) { - ALOGE("Error connecting to supplicant (%s)\n", strerror(errno)); - return -1; - } - - if (retrieveInterfaceName()) { - ALOGE("Error retrieving interface name (%s)\n", strerror(errno)); - return -1; - } - - return 0; -} - -int Supplicant::stop() { - - if (mListener->stopListener()) { - ALOGW("Unable to stop supplicant listener (%s)", strerror(errno)); - return -1; - } - - if (mServiceManager->stop(SUPPLICANT_SERVICE_NAME)) { - ALOGW("Error stopping supplicant (%s)", strerror(errno)); - } - - if (mCtrl) { - wpa_ctrl_close(mCtrl); - mCtrl = NULL; - } - if (mMonitor) { - wpa_ctrl_close(mMonitor); - mMonitor = NULL; - } - - return 0; -} - -bool Supplicant::isStarted() { - return mServiceManager->isRunning(SUPPLICANT_SERVICE_NAME); -} - -int Supplicant::sendCommand(const char *cmd, char *reply, size_t *reply_len) { - - if (!mCtrl) { - errno = ENOTCONN; - return -1; - } - -// ALOGD("sendCommand(): -> '%s'", cmd); - - int rc; - memset(reply, 0, *reply_len); - if ((rc = wpa_ctrl_request(mCtrl, cmd, strlen(cmd), reply, reply_len, NULL)) == -2) { - errno = ETIMEDOUT; - return -1; - } else if (rc < 0 || !strncmp(reply, "FAIL", 4)) { - strcpy(reply, "FAIL"); - errno = EIO; - return -1; - } - - // ALOGD("sendCommand(): <- '%s'", reply); - return 0; -} -SupplicantStatus *Supplicant::getStatus() { - char *reply; - size_t len = 4096; - - if (!(reply = (char *) malloc(len))) { - errno = ENOMEM; - return NULL; - } - - if (sendCommand("STATUS", reply, &len)) { - free(reply); - return NULL; - } - - SupplicantStatus *ss = SupplicantStatus::createStatus(reply, len); - - free (reply); - return ss; -} - -/* - * Retrieves the list of networks from Supplicant - * and merge them into our current list - */ -int Supplicant::refreshNetworkList() { - char *reply; - size_t len = 4096; - - if (!(reply = (char *) malloc(len))) { - errno = ENOMEM; - return -1; - } - - if (sendCommand("LIST_NETWORKS", reply, &len)) { - free(reply); - return -1; - } - - char *linep; - char *linep_next = NULL; - - if (!strtok_r(reply, "\n", &linep_next)) { - ALOGW("Malformatted network list\n"); - free(reply); - errno = EIO; - return -1; - } - - PropertyManager *pm = NetworkManager::Instance()->getPropMngr(); - pthread_mutex_lock(&mNetworksLock); - - int num_added = 0; - int num_refreshed = 0; - int num_removed = 0; - while((linep = strtok_r(NULL, "\n", &linep_next))) { - // TODO: Move the decode into a static method so we - // don't create new_wn when we don't have to. - WifiNetwork *new_wn = new WifiNetwork(mController, this, linep); - WifiNetwork *merge_wn; - - if ((merge_wn = this->lookupNetwork_UNLOCKED(new_wn->getNetworkId()))) { - num_refreshed++; - if (merge_wn->refresh()) { - ALOGW("Error refreshing network %d (%s)", - merge_wn->getNetworkId(), strerror(errno)); - } - delete new_wn; - } else { - num_added++; - char new_ns[20]; - snprintf(new_ns, sizeof(new_ns), "wifi.net.%d", new_wn->getNetworkId()); - new_wn->attachProperties(pm, new_ns); - mNetworks->push_back(new_wn); - if (new_wn->refresh()) { - ALOGW("Unable to refresh network id %d (%s)", - new_wn->getNetworkId(), strerror(errno)); - } - } - } - - if (!mNetworks->empty()) { - // TODO: Add support for detecting removed networks - WifiNetworkCollection::iterator i; - - for (i = mNetworks->begin(); i != mNetworks->end(); ++i) { - if (0) { - num_removed++; - char del_ns[20]; - snprintf(del_ns, sizeof(del_ns), "wifi.net.%d", (*i)->getNetworkId()); - (*i)->detachProperties(pm, del_ns); - delete (*i); - i = mNetworks->erase(i); - } - } - } - - - ALOGD("Networks added %d, refreshed %d, removed %d\n", - num_added, num_refreshed, num_removed); - pthread_mutex_unlock(&mNetworksLock); - - free(reply); - return 0; -} - -int Supplicant::connectToSupplicant() { - if (!isStarted()) - ALOGW("Supplicant service not running"); - - mCtrl = wpa_ctrl_open("tiwlan0"); // XXX: - if (mCtrl == NULL) { - ALOGE("Unable to open connection to supplicant on \"%s\": %s", - "tiwlan0", strerror(errno)); - return -1; - } - mMonitor = wpa_ctrl_open("tiwlan0"); - if (mMonitor == NULL) { - wpa_ctrl_close(mCtrl); - mCtrl = NULL; - return -1; - } - if (wpa_ctrl_attach(mMonitor) != 0) { - wpa_ctrl_close(mMonitor); - wpa_ctrl_close(mCtrl); - mCtrl = mMonitor = NULL; - return -1; - } - - mListener = new SupplicantListener(mHandlers, mMonitor); - - if (mListener->startListener()) { - ALOGE("Error - unable to start supplicant listener"); - stop(); - return -1; - } - return 0; -} - -int Supplicant::setScanMode(bool active) { - char reply[255]; - size_t len = sizeof(reply); - - if (sendCommand((active ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"), - reply, &len)) { - ALOGW("triggerScan(%d): Error setting scan mode (%s)", active, - strerror(errno)); - return -1; - } - return 0; -} - -int Supplicant::triggerScan() { - char reply[255]; - size_t len = sizeof(reply); - - if (sendCommand("SCAN", reply, &len)) { - ALOGW("triggerScan(): Error initiating scan"); - return -1; - } - return 0; -} - -int Supplicant::getRssi(int *buffer) { - char reply[64]; - size_t len = sizeof(reply); - - if (sendCommand("DRIVER RSSI", reply, &len)) { - ALOGW("Failed to get RSSI (%s)", strerror(errno)); - return -1; - } - - char *next = reply; - char *s; - for (int i = 0; i < 3; i++) { - if (!(s = strsep(&next, " "))) { - ALOGE("Error parsing RSSI"); - errno = EIO; - return -1; - } - } - *buffer = atoi(s); - return 0; -} - -int Supplicant::getLinkSpeed() { - char reply[64]; - size_t len = sizeof(reply); - - if (sendCommand("DRIVER LINKSPEED", reply, &len)) { - ALOGW("Failed to get LINKSPEED (%s)", strerror(errno)); - return -1; - } - - char *next = reply; - char *s; - - if (!(s = strsep(&next, " "))) { - ALOGE("Error parsing LINKSPEED"); - errno = EIO; - return -1; - } - - if (!(s = strsep(&next, " "))) { - ALOGE("Error parsing LINKSPEED"); - errno = EIO; - return -1; - } - return atoi(s); -} - -int Supplicant::stopDriver() { - char reply[64]; - size_t len = sizeof(reply); - - ALOGD("stopDriver()"); - - if (sendCommand("DRIVER STOP", reply, &len)) { - ALOGW("Failed to stop driver (%s)", strerror(errno)); - return -1; - } - return 0; -} - -int Supplicant::startDriver() { - char reply[64]; - size_t len = sizeof(reply); - - ALOGD("startDriver()"); - if (sendCommand("DRIVER START", reply, &len)) { - ALOGW("Failed to start driver (%s)", strerror(errno)); - return -1; - } - return 0; -} - -WifiNetwork *Supplicant::createNetwork() { - char reply[255]; - size_t len = sizeof(reply) -1; - - if (sendCommand("ADD_NETWORK", reply, &len)) - return NULL; - - if (reply[strlen(reply) -1] == '\n') - reply[strlen(reply) -1] = '\0'; - - WifiNetwork *wn = new WifiNetwork(mController, this, atoi(reply)); - PropertyManager *pm = NetworkManager::Instance()->getPropMngr(); - pthread_mutex_lock(&mNetworksLock); - char new_ns[20]; - snprintf(new_ns, sizeof(new_ns), "wifi.net.%d", wn->getNetworkId()); - wn->attachProperties(pm, new_ns); - mNetworks->push_back(wn); - pthread_mutex_unlock(&mNetworksLock); - return wn; -} - -int Supplicant::removeNetwork(WifiNetwork *wn) { - char req[64]; - - sprintf(req, "REMOVE_NETWORK %d", wn->getNetworkId()); - char reply[32]; - size_t len = sizeof(reply) -1; - - if (sendCommand(req, reply, &len)) - return -1; - - pthread_mutex_lock(&mNetworksLock); - WifiNetworkCollection::iterator it; - for (it = mNetworks->begin(); it != mNetworks->end(); ++it) { - if ((*it) == wn) { - mNetworks->erase(it); - break; - } - } - pthread_mutex_unlock(&mNetworksLock); - return 0; -} - -WifiNetwork *Supplicant::lookupNetwork(int networkId) { - pthread_mutex_lock(&mNetworksLock); - WifiNetwork *wn = lookupNetwork_UNLOCKED(networkId); - pthread_mutex_unlock(&mNetworksLock); - return wn; -} - -WifiNetwork *Supplicant::lookupNetwork_UNLOCKED(int networkId) { - WifiNetworkCollection::iterator it; - for (it = mNetworks->begin(); it != mNetworks->end(); ++it) { - if ((*it)->getNetworkId() == networkId) { - return *it; - } - } - errno = ENOENT; - return NULL; -} - -WifiNetworkCollection *Supplicant::createNetworkList() { - WifiNetworkCollection *d = new WifiNetworkCollection(); - WifiNetworkCollection::iterator i; - - pthread_mutex_lock(&mNetworksLock); - for (i = mNetworks->begin(); i != mNetworks->end(); ++i) - d->push_back((*i)->clone()); - - pthread_mutex_unlock(&mNetworksLock); - return d; -} - -int Supplicant::setupConfig() { - char buf[2048]; - int srcfd, destfd; - int nread; - - if (access(SUPP_CONFIG_FILE, R_OK|W_OK) == 0) { - return 0; - } else if (errno != ENOENT) { - ALOGE("Cannot access \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno)); - return -1; - } - - srcfd = open(SUPP_CONFIG_TEMPLATE, O_RDONLY); - if (srcfd < 0) { - ALOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno)); - return -1; - } - - destfd = open(SUPP_CONFIG_FILE, O_CREAT|O_WRONLY, 0660); - if (destfd < 0) { - close(srcfd); - ALOGE("Cannot create \"%s\": %s", SUPP_CONFIG_FILE, strerror(errno)); - return -1; - } - - while ((nread = read(srcfd, buf, sizeof(buf))) != 0) { - if (nread < 0) { - ALOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno)); - close(srcfd); - close(destfd); - unlink(SUPP_CONFIG_FILE); - return -1; - } - write(destfd, buf, nread); - } - - close(destfd); - close(srcfd); - - if (chown(SUPP_CONFIG_FILE, AID_SYSTEM, AID_WIFI) < 0) { - ALOGE("Error changing group ownership of %s to %d: %s", - SUPP_CONFIG_FILE, AID_WIFI, strerror(errno)); - unlink(SUPP_CONFIG_FILE); - return -1; - } - return 0; -} - -int Supplicant::setNetworkVar(int networkId, const char *var, const char *val) { - char reply[255]; - size_t len = sizeof(reply) -1; - - ALOGD("netid %d, var '%s' = '%s'", networkId, var, val); - char *tmp; - asprintf(&tmp, "SET_NETWORK %d %s %s", networkId, var, val); - if (sendCommand(tmp, reply, &len)) { - free(tmp); - return -1; - } - free(tmp); - - len = sizeof(reply) -1; - if (sendCommand("SAVE_CONFIG", reply, &len)) { - ALOGE("Error saving config after %s = %s", var, val); - return -1; - } - return 0; -} - -const char *Supplicant::getNetworkVar(int networkId, const char *var, - char *buffer, size_t max) { - size_t len = max - 1; - char *tmp; - - asprintf(&tmp, "GET_NETWORK %d %s", networkId, var); - if (sendCommand(tmp, buffer, &len)) { - free(tmp); - return NULL; - } - free(tmp); - return buffer; -} - -int Supplicant::enableNetwork(int networkId, bool enabled) { - char req[64]; - - if (enabled) - sprintf(req, "ENABLE_NETWORK %d", networkId); - else - sprintf(req, "DISABLE_NETWORK %d", networkId); - - char reply[16]; - size_t len = sizeof(reply) -1; - - if (sendCommand(req, reply, &len)) - return -1; - return 0; -} - -int Supplicant::enablePacketFilter() { - char req[128]; - char reply[16]; - size_t len; - int i; - - for (i = 0; i <=3; i++) { - snprintf(req, sizeof(req), "DRIVER RXFILTER-ADD %d", i); - len = sizeof(reply); - if (sendCommand(req, reply, &len)) - return -1; - } - - len = sizeof(reply); - if (sendCommand("DRIVER RXFILTER-START", reply, &len)) - return -1; - return 0; -} - -int Supplicant::disablePacketFilter() { - char req[128]; - char reply[16]; - size_t len; - int i; - - len = sizeof(reply); - if (sendCommand("DRIVER RXFILTER-STOP", reply, &len)) - return -1; - - for (i = 3; i >=0; i--) { - snprintf(req, sizeof(req), "DRIVER RXFILTER-REMOVE %d", i); - len = sizeof(reply); - if (sendCommand(req, reply, &len)) - return -1; - } - return 0; -} - -int Supplicant::enableBluetoothCoexistenceScan() { - char req[128]; - char reply[16]; - size_t len; - int i; - - len = sizeof(reply); - if (sendCommand("DRIVER BTCOEXSCAN-START", reply, &len)) - return -1; - return 0; -} - -int Supplicant::disableBluetoothCoexistenceScan() { - char req[128]; - char reply[16]; - size_t len; - int i; - - len = sizeof(reply); - if (sendCommand("DRIVER BTCOEXSCAN-STOP", reply, &len)) - return -1; - return 0; -} - -int Supplicant::setBluetoothCoexistenceMode(int mode) { - char req[64]; - - sprintf(req, "DRIVER BTCOEXMODE %d", mode); - - char reply[16]; - size_t len = sizeof(reply) -1; - - if (sendCommand(req, reply, &len)) - return -1; - return 0; -} - -int Supplicant::setApScanMode(int mode) { - char req[64]; - -// ALOGD("setApScanMode(%d)", mode); - sprintf(req, "AP_SCAN %d", mode); - - char reply[16]; - size_t len = sizeof(reply) -1; - - if (sendCommand(req, reply, &len)) - return -1; - return 0; -} - - -int Supplicant::retrieveInterfaceName() { - char reply[255]; - size_t len = sizeof(reply) -1; - - if (sendCommand("INTERFACES", reply, &len)) - return -1; - - reply[strlen(reply)-1] = '\0'; - mInterfaceName = strdup(reply); - return 0; -} - -int Supplicant::reconnect() { - char req[128]; - char reply[16]; - size_t len; - int i; - - len = sizeof(reply); - if (sendCommand("RECONNECT", reply, &len)) - return -1; - return 0; -} - -int Supplicant::disconnect() { - char req[128]; - char reply[16]; - size_t len; - int i; - - len = sizeof(reply); - if (sendCommand("DISCONNECT", reply, &len)) - return -1; - return 0; -} - -int Supplicant::getNetworkCount() { - pthread_mutex_lock(&mNetworksLock); - int cnt = mNetworks->size(); - pthread_mutex_unlock(&mNetworksLock); - return cnt; -} diff --git a/nexus/Supplicant.h b/nexus/Supplicant.h deleted file mode 100644 index 3900f4f..0000000 --- a/nexus/Supplicant.h +++ /dev/null @@ -1,95 +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. - */ - -#ifndef _SUPPLICANT_H -#define _SUPPLICANT_H - -struct wpa_ctrl; -class SupplicantListener; -class ServiceManager; -class Controller; -class WifiController; -class SupplicantStatus; - -#include <pthread.h> - -#include "WifiNetwork.h" -#include "ISupplicantEventHandler.h" - -class Supplicant { - struct wpa_ctrl *mCtrl; - struct wpa_ctrl *mMonitor; - SupplicantListener *mListener; - ServiceManager *mServiceManager; - WifiController *mController; - char *mInterfaceName; - - WifiNetworkCollection *mNetworks; - pthread_mutex_t mNetworksLock; - ISupplicantEventHandler *mHandlers; - -public: - Supplicant(WifiController *wc, ISupplicantEventHandler *handlers); - virtual ~Supplicant(); - - int start(); - int stop(); - bool isStarted(); - - int setScanMode(bool active); - int triggerScan(); - - WifiNetwork *createNetwork(); - WifiNetwork *lookupNetwork(int networkId); - int removeNetwork(WifiNetwork *net); - WifiNetworkCollection *createNetworkList(); - int refreshNetworkList(); - - int setNetworkVar(int networkId, const char *var, const char *value); - const char *getNetworkVar(int networkid, const char *var, char *buffer, - size_t max); - int enableNetwork(int networkId, bool enabled); - - int disconnect(); - int reconnect(); - int reassociate(); - int setApScanMode(int mode); - int enablePacketFilter(); - int disablePacketFilter(); - int setBluetoothCoexistenceMode(int mode); - int enableBluetoothCoexistenceScan(); - int disableBluetoothCoexistenceScan(); - int stopDriver(); - int startDriver(); - int getRssi(int *buffer); - int getLinkSpeed(); - int getNetworkCount(); - - SupplicantStatus *getStatus(); - - Controller *getController() { return (Controller *) mController; } - const char *getInterfaceName() { return mInterfaceName; } - - int sendCommand(const char *cmd, char *reply, size_t *reply_len); - -private: - int connectToSupplicant(); - int setupConfig(); - int retrieveInterfaceName(); - WifiNetwork *lookupNetwork_UNLOCKED(int networkId); -}; - -#endif diff --git a/nexus/SupplicantAssociatedEvent.cpp b/nexus/SupplicantAssociatedEvent.cpp deleted file mode 100644 index e40411e..0000000 --- a/nexus/SupplicantAssociatedEvent.cpp +++ /dev/null @@ -1,40 +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. - */ - -#include <stdlib.h> - -#define LOG_TAG "SupplicantAssociatedEvent" -#include <cutils/log.h> - -#include "SupplicantAssociatedEvent.h" - -SupplicantAssociatedEvent::SupplicantAssociatedEvent(int level, char *event, - size_t len) : - SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATED, - level) { - char *p = event; - - // "00:13:46:40:40:aa" - mBssid = (char *) malloc(18); - strncpy(mBssid, p, 17); - mBssid[17] = '\0'; -} - -SupplicantAssociatedEvent::~SupplicantAssociatedEvent() { - if (mBssid) - free(mBssid); -} - diff --git a/nexus/SupplicantAssociatingEvent.cpp b/nexus/SupplicantAssociatingEvent.cpp deleted file mode 100644 index 8fe502e..0000000 --- a/nexus/SupplicantAssociatingEvent.cpp +++ /dev/null @@ -1,100 +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. - */ - -#include <stdlib.h> - -#define LOG_TAG "SupplicantAssociatingEvent" -#include <cutils/log.h> - -#include "SupplicantAssociatingEvent.h" - -SupplicantAssociatingEvent::SupplicantAssociatingEvent(int level, char *event, - size_t len) : - SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATING, - level) { - char *p = event; - - mBssid = NULL; - mSsid = NULL; - mFreq = -1; - - // SSID 'default' - // OR - // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)" - - if (strncmp(event, "SSID", 4)) { - mBssid = (char *) malloc(18); - strncpy(mBssid, p, 17); - mBssid[17] = '\0'; - p += 25; - - // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)" - // ^ - // p - char *q = index(p, '\''); - if (!q) { - ALOGE("Unable to decode SSID (p = {%s})\n", p); - return; - } - mSsid = (char *) malloc((q - p) +1); - strncpy(mSsid, p, q-p); - mSsid[q-p] = '\0'; - - p = q + 7; - - // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)" - // ^ - // p - if (!(q = index(p, ' '))) { - ALOGE("Unable to decode frequency\n"); - return; - } - *q = '\0'; - mFreq = atoi(p); - } else { - p+= 6; - - // SSID 'default' - // ^ - // p - - char *q = index(p, '\''); - if (!q) { - ALOGE("Unable to decode SSID (p = {%s})\n", p); - return; - } - mSsid = (char *) malloc((q - p) +1); - strncpy(mSsid, p, q-p); - mSsid[q-p] = '\0'; - } -} - -SupplicantAssociatingEvent::SupplicantAssociatingEvent(const char *bssid, - const char *ssid, - int freq) : - SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATING, -1) { - mBssid = strdup(bssid); - mSsid= strdup(ssid); - mFreq = freq; -} - -SupplicantAssociatingEvent::~SupplicantAssociatingEvent() { - if (mBssid) - free(mBssid); - if (mSsid) - free(mSsid); -} - diff --git a/nexus/SupplicantAssociatingEvent.h b/nexus/SupplicantAssociatingEvent.h deleted file mode 100644 index d3a4d5c..0000000 --- a/nexus/SupplicantAssociatingEvent.h +++ /dev/null @@ -1,37 +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. - */ - -#ifndef _SupplicantAssociatingEvent_H -#define _SupplicantAssociatingEvent_H - -#include "SupplicantEvent.h" - -class SupplicantAssociatingEvent : public SupplicantEvent { - char *mBssid; - char *mSsid; - int mFreq; - -public: - SupplicantAssociatingEvent(int level, char *event, size_t len); - SupplicantAssociatingEvent(const char *bssid, const char *ssid, int freq); - virtual ~SupplicantAssociatingEvent(); - - const char *getBssid() { return mBssid; } - const char *getSsid() { return mSsid; } - int getFreq() { return mFreq;} -}; - -#endif diff --git a/nexus/SupplicantConnectedEvent.cpp b/nexus/SupplicantConnectedEvent.cpp deleted file mode 100644 index 107c766..0000000 --- a/nexus/SupplicantConnectedEvent.cpp +++ /dev/null @@ -1,61 +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. - */ - -#define LOG_TAG "SupplicantConnectedEvent" -#include <cutils/log.h> - -#include "SupplicantConnectedEvent.h" - -SupplicantConnectedEvent::SupplicantConnectedEvent(int level, char *event, - size_t len) : - SupplicantEvent(SupplicantEvent::EVENT_CONNECTED, - level) { - char *p; - - // "- Connection to 00:13:46:40:40:aa completed (auth) [id=1 id_str=], 89" - - if ((p = index(event + 2, ' ')) && (++p = index(p, ' '))) { - mBssid = (char *) malloc(18); - strncpy(mBssid, ++p, 17); - mBssid[17] = '\0'; - - // "- Connection to 00:13:46:40:40:aa completed (auth) [id=1 id_str=], 89" - // ^ - // p - - if ((p = index(p, ' ')) && ((++p = index(p, ' ')))) { - if (!strncmp(++p, "(auth)", 6)) - mReassociated = false; - else - mReassociated = true; - } else - ALOGE("Unable to decode re-assocation"); - } else - ALOGE("Unable to decode event"); -} - -SupplicantConnectedEvent::SupplicantConnectedEvent(const char *bssid, - bool reassocated) : - SupplicantEvent(SupplicantEvent::EVENT_CONNECTED, -1) { - mBssid = strdup(bssid); - mReassociated = reassocated; -} - -SupplicantConnectedEvent::~SupplicantConnectedEvent() { - if (mBssid) - free(mBssid); -} - diff --git a/nexus/SupplicantConnectedEvent.h b/nexus/SupplicantConnectedEvent.h deleted file mode 100644 index 03e9842..0000000 --- a/nexus/SupplicantConnectedEvent.h +++ /dev/null @@ -1,36 +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. - */ - -#ifndef _SupplicantConnectedEvent_H -#define _SupplicantConnectedEvent_H - -#include "SupplicantEvent.h" - -class SupplicantConnectedEvent : public SupplicantEvent { -private: - char *mBssid; - bool mReassociated; - -public: - SupplicantConnectedEvent(int level, char *event, size_t len); - SupplicantConnectedEvent(const char *bssid, bool reassicated); - virtual ~SupplicantConnectedEvent(); - - const char *getBssid() { return mBssid; } - bool getReassociated() { return mReassociated; } -}; - -#endif diff --git a/nexus/SupplicantConnectionTimeoutEvent.cpp b/nexus/SupplicantConnectionTimeoutEvent.cpp deleted file mode 100644 index 8b8a7d7..0000000 --- a/nexus/SupplicantConnectionTimeoutEvent.cpp +++ /dev/null @@ -1,36 +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. - */ - -#define LOG_TAG "SupplicantConnectionTimeoutEvent" -#include <cutils/log.h> - -#include "SupplicantConnectionTimeoutEvent.h" - -SupplicantConnectionTimeoutEvent::SupplicantConnectionTimeoutEvent(int level, char *event, - size_t len) : - SupplicantEvent(SupplicantEvent::EVENT_CONNECTIONTIMEOUT, - level) { - // 00:13:46:40:40:aa timed out.' - mBssid = (char *) malloc(18); - strncpy(mBssid, event, 17); - mBssid[17] = '\0'; -} - -SupplicantConnectionTimeoutEvent::~SupplicantConnectionTimeoutEvent() { - if (mBssid) - free(mBssid); -} - diff --git a/nexus/SupplicantConnectionTimeoutEvent.h b/nexus/SupplicantConnectionTimeoutEvent.h deleted file mode 100644 index 0d2606d..0000000 --- a/nexus/SupplicantConnectionTimeoutEvent.h +++ /dev/null @@ -1,34 +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. - */ - -#ifndef _SupplicantConnectionTimeoutEvent_H -#define _SupplicantConnectionTimeoutEvent_H - -#include "SupplicantEvent.h" - -class SupplicantConnectionTimeoutEvent : public SupplicantEvent { -private: - char *mBssid; - bool mReassociated; - -public: - SupplicantConnectionTimeoutEvent(int level, char *event, size_t len); - virtual ~SupplicantConnectionTimeoutEvent(); - - const char *getBssid() { return mBssid; } -}; - -#endif diff --git a/nexus/SupplicantDisconnectedEvent.cpp b/nexus/SupplicantDisconnectedEvent.cpp deleted file mode 100644 index 11e499d..0000000 --- a/nexus/SupplicantDisconnectedEvent.cpp +++ /dev/null @@ -1,33 +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. - */ - -#define LOG_TAG "SupplicantDisconnectedEvent" -#include <cutils/log.h> - -#include "SupplicantDisconnectedEvent.h" - -SupplicantDisconnectedEvent::SupplicantDisconnectedEvent(int level, char *event, - size_t len) : - SupplicantEvent(SupplicantEvent::EVENT_DISCONNECTED, - level) { -} - -SupplicantDisconnectedEvent::SupplicantDisconnectedEvent() : - SupplicantEvent(SupplicantEvent::EVENT_DISCONNECTED, -1) { -} - -SupplicantDisconnectedEvent::~SupplicantDisconnectedEvent() { -} diff --git a/nexus/SupplicantEvent.h b/nexus/SupplicantEvent.h deleted file mode 100644 index 9d7cbd9..0000000 --- a/nexus/SupplicantEvent.h +++ /dev/null @@ -1,54 +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. - */ - -#ifndef _SUPPLICANT_EVENT_H -#define _SUPPLICANT_EVENT_H - -#include <sys/types.h> - -class SupplicantEvent { -private: - int mType; - int mLevel; - -public: - static const int EVENT_UNKNOWN = 0; - static const int EVENT_CONNECTED = 1; - static const int EVENT_DISCONNECTED = 2; - static const int EVENT_TERMINATING = 3; - static const int EVENT_PASSWORD_CHANGED = 4; - static const int EVENT_EAP_NOTIFICATION = 5; - static const int EVENT_EAP_STARTED = 6; - static const int EVENT_EAP_METHOD = 7; - static const int EVENT_EAP_SUCCESS = 8; - static const int EVENT_EAP_FAILURE = 9; - static const int EVENT_SCAN_RESULTS = 10; - static const int EVENT_STATE_CHANGE = 11; - static const int EVENT_LINK_SPEED = 12; - static const int EVENT_DRIVER_STATE = 13; - static const int EVENT_ASSOCIATING = 14; - static const int EVENT_ASSOCIATED = 15; - static const int EVENT_CONNECTIONTIMEOUT = 16; - -public: - SupplicantEvent(int type, int level); - virtual ~SupplicantEvent() {} - - int getType() { return mType; } - int getLevel() { return mLevel; } -}; - -#endif diff --git a/nexus/SupplicantEventFactory.cpp b/nexus/SupplicantEventFactory.cpp deleted file mode 100644 index f91db15..0000000 --- a/nexus/SupplicantEventFactory.cpp +++ /dev/null @@ -1,119 +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. - */ - -#include <stdlib.h> - -#define LOG_TAG "SupplicantEventFactory" -#include <cutils/log.h> - -#include "SupplicantEvent.h" -#include "SupplicantEventFactory.h" -#include "SupplicantAssociatingEvent.h" -#include "SupplicantAssociatedEvent.h" -#include "SupplicantConnectedEvent.h" -#include "SupplicantStateChangeEvent.h" -#include "SupplicantScanResultsEvent.h" -#include "SupplicantConnectionTimeoutEvent.h" -#include "SupplicantDisconnectedEvent.h" -#if 0 -#include "SupplicantTerminatingEvent.h" -#include "SupplicantPasswordChangedEvent.h" -#include "SupplicantEapNotificationEvent.h" -#include "SupplicantEapStartedEvent.h" -#include "SupplicantEapMethodEvent.h" -#include "SupplicantEapSuccessEvent.h" -#include "SupplicantEapFailureEvent.h" -#include "SupplicantLinkSpeedEvent.h" -#include "SupplicantDriverStateEvent.h" -#endif - -#include "libwpa_client/wpa_ctrl.h" - -SupplicantEventFactory::SupplicantEventFactory() { -} - -SupplicantEvent *SupplicantEventFactory::createEvent(char *event, size_t len) { - int level = 0; - - if (event[0] == '<') { - char *match = strchr(event, '>'); - if (match) { - char tmp[16]; - - strncpy(tmp, &event[1], (match - event)); - level = atoi(tmp); - event += (match - event) + 1; - } else - ALOGW("Unclosed level brace in event"); - } else - ALOGW("No level specified in event"); - - /* - * <N>CTRL-EVENT-XXX - * ^ - * +---- event - */ - - if (!strncmp(event, "Authentication with ", 20)) { - if (!strcmp(event + strlen(event) - strlen(" timed out."), - " timed out.")) { - return new SupplicantConnectionTimeoutEvent(level, - event + 20, - len); - } else - return NULL; - - } else if (!strncmp(event, "Associated with ", 16)) - return new SupplicantAssociatedEvent(level, event + 16, len); - else if (!strncmp(event, "Trying to associate with ", 25)) - return new SupplicantAssociatingEvent(level, event + 25, len); - else if (!strncmp(event, WPA_EVENT_CONNECTED, strlen(WPA_EVENT_CONNECTED))) { - return new SupplicantConnectedEvent(level, - event + strlen(WPA_EVENT_CONNECTED), - len); - } else if (!strncmp(event, WPA_EVENT_SCAN_RESULTS, strlen(WPA_EVENT_SCAN_RESULTS))) { - return new SupplicantScanResultsEvent(level, - event + strlen(WPA_EVENT_SCAN_RESULTS), - len); - } else if (!strncmp(event, WPA_EVENT_STATE_CHANGE, strlen(WPA_EVENT_STATE_CHANGE))) { - return new SupplicantStateChangeEvent(level, - event + strlen(WPA_EVENT_STATE_CHANGE), - len); - } - else if (!strncmp(event, WPA_EVENT_DISCONNECTED, strlen(WPA_EVENT_DISCONNECTED))) - return new SupplicantDisconnectedEvent(level, event, len); -#if 0 - else if (!strncmp(event, WPA_EVENT_TERMINATING, strlen(WPA_EVENT_TERMINATING))) - return new SupplicantTerminatingEvent(event, len); - else if (!strncmp(event, WPA_EVENT_PASSWORD_CHANGED, strlen(WPA_EVENT_PASSWORD_CHANGED))) - return new SupplicantPasswordChangedEvent(event, len); - else if (!strncmp(event, WPA_EVENT_EAP_NOTIFICATION, strlen(WPA_EVENT_EAP_NOTIFICATION))) - return new SupplicantEapNotificationEvent(event, len); - else if (!strncmp(event, WPA_EVENT_EAP_STARTED, strlen(WPA_EVENT_EAP_STARTED))) - return new SupplicantEapStartedEvent(event, len); - else if (!strncmp(event, WPA_EVENT_EAP_METHOD, strlen(WPA_EVENT_EAP_METHOD))) - return new SupplicantEapMethodEvent(event, len); - else if (!strncmp(event, WPA_EVENT_EAP_SUCCESS, strlen(WPA_EVENT_EAP_SUCCESS))) - return new SupplicantEapSuccessEvent(event, len); - else if (!strncmp(event, WPA_EVENT_EAP_FAILURE, strlen(WPA_EVENT_EAP_FAILURE))) - return new SupplicantEapFailureEvent(event, len); - else if (!strncmp(event, WPA_EVENT_LINK_SPEED, strlen(WPA_EVENT_LINK_SPEED))) - return new SupplicantLinkSpeedEvent(event, len); - else if (!strncmp(event, WPA_EVENT_DRIVER_STATE, strlen(WPA_EVENT_DRIVER_STATE))) - return new SupplicantDriverStateEvent(event, len); -#endif - return NULL; -} diff --git a/nexus/SupplicantListener.cpp b/nexus/SupplicantListener.cpp deleted file mode 100644 index 421d84d..0000000 --- a/nexus/SupplicantListener.cpp +++ /dev/null @@ -1,109 +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. - */ - -#include <errno.h> -#include <sys/types.h> -#include <pthread.h> - -#define LOG_TAG "SupplicantListener" -#include <cutils/log.h> - -#include "libwpa_client/wpa_ctrl.h" - -#include "SupplicantListener.h" -#include "ISupplicantEventHandler.h" -#include "SupplicantEventFactory.h" -#include "SupplicantEvent.h" -#include "SupplicantAssociatingEvent.h" -#include "SupplicantAssociatedEvent.h" -#include "SupplicantConnectedEvent.h" -#include "SupplicantScanResultsEvent.h" -#include "SupplicantStateChangeEvent.h" - -SupplicantListener::SupplicantListener(ISupplicantEventHandler *handlers, - struct wpa_ctrl *monitor) : - SocketListener(wpa_ctrl_get_fd(monitor), false) { - mHandlers = handlers; - mMonitor = monitor; - mFactory = new SupplicantEventFactory(); -} - -bool SupplicantListener::onDataAvailable(SocketClient *cli) { - char buf[255]; - size_t buflen = sizeof(buf); - int rc; - size_t nread = buflen - 1; - - if ((rc = wpa_ctrl_recv(mMonitor, buf, &nread))) { - ALOGE("wpa_ctrl_recv failed (%s)", strerror(errno)); - return false; - } - - buf[nread] = '\0'; - if (!rc && !nread) { - ALOGD("Received EOF on supplicant socket\n"); - strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", buflen-1); - buf[buflen-1] = '\0'; - return false; - } - - SupplicantEvent *evt = mFactory->createEvent(buf, nread); - - if (!evt) { - ALOGW("Dropping unknown supplicant event '%s'", buf); - return true; - } - - // Call the appropriate handler - if (evt->getType() == SupplicantEvent::EVENT_ASSOCIATING) - mHandlers->onAssociatingEvent((SupplicantAssociatingEvent *) evt); - else if (evt->getType() == SupplicantEvent::EVENT_ASSOCIATED) - mHandlers->onAssociatedEvent((SupplicantAssociatedEvent *) evt); - else if (evt->getType() == SupplicantEvent::EVENT_CONNECTED) - mHandlers->onConnectedEvent((SupplicantConnectedEvent *) evt); - else if (evt->getType() == SupplicantEvent::EVENT_SCAN_RESULTS) - mHandlers->onScanResultsEvent((SupplicantScanResultsEvent *) evt); - else if (evt->getType() == SupplicantEvent::EVENT_STATE_CHANGE) - mHandlers->onStateChangeEvent((SupplicantStateChangeEvent *) evt); - else if (evt->getType() == SupplicantEvent::EVENT_CONNECTIONTIMEOUT) - mHandlers->onConnectionTimeoutEvent((SupplicantConnectionTimeoutEvent *) evt); - else if (evt->getType() == SupplicantEvent::EVENT_DISCONNECTED) - mHandlers->onDisconnectedEvent((SupplicantDisconnectedEvent *) evt); - else - ALOGW("Whoops - no handler available for event '%s'\n", buf); -#if 0 - else if (evt->getType() == SupplicantEvent::EVENT_TERMINATING) - mHandlers->onTerminatingEvent(evt); - else if (evt->getType() == SupplicantEvent::EVENT_PASSWORD_CHANGED) - mHandlers->onPasswordChangedEvent(evt); - else if (evt->getType() == SupplicantEvent::EVENT_EAP_NOTIFICATION) - mHandlers->onEapNotificationEvent(evt); - else if (evt->getType() == SupplicantEvent::EVENT_EAP_STARTED) - mHandlers->onEapStartedEvent(evt); - else if (evt->getType() == SupplicantEvent::EVENT_EAP_SUCCESS) - mHandlers->onEapSuccessEvent(evt); - else if (evt->getType() == SupplicantEvent::EVENT_EAP_FAILURE) - mHandlers->onEapFailureEvent(evt); - else if (evt->getType() == SupplicantEvent::EVENT_LINK_SPEED) - mHandlers->onLinkSpeedEvent(evt); - else if (evt->getType() == SupplicantEvent::EVENT_DRIVER_STATE) - mHandlers->onDriverStateEvent(evt); -#endif - - delete evt; - - return true; -} diff --git a/nexus/SupplicantListener.h b/nexus/SupplicantListener.h deleted file mode 100644 index a1da773..0000000 --- a/nexus/SupplicantListener.h +++ /dev/null @@ -1,44 +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. - */ - -#ifndef _SUPPLICANTLISTENER_H__ -#define _SUPPLICANTLISTENER_H__ - -#include <sysutils/SocketListener.h> - -struct wpa_ctrl; -class Supplicant; -class SocketClient; -class ISupplicantEventHandler; -class SupplicantEventFactory; - -class SupplicantListener: public SocketListener { - struct wpa_ctrl *mMonitor; - ISupplicantEventHandler *mHandlers; - SupplicantEventFactory *mFactory; - -public: - SupplicantListener(ISupplicantEventHandler *handlers, - struct wpa_ctrl *monitor); - virtual ~SupplicantListener() {} - - struct wpa_ctrl *getMonitor() { return mMonitor; } - -protected: - virtual bool onDataAvailable(SocketClient *c); -}; - -#endif diff --git a/nexus/SupplicantScanResultsEvent.cpp b/nexus/SupplicantScanResultsEvent.cpp deleted file mode 100644 index c53adad..0000000 --- a/nexus/SupplicantScanResultsEvent.cpp +++ /dev/null @@ -1,34 +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. - */ - -#define LOG_TAG "SupplicantScanResultsEvent" -#include <cutils/log.h> - -#include "SupplicantScanResultsEvent.h" - -SupplicantScanResultsEvent::SupplicantScanResultsEvent(int level, char *event, - size_t len) : - SupplicantEvent(SupplicantEvent::EVENT_SCAN_RESULTS, - level) { -} - -SupplicantScanResultsEvent::SupplicantScanResultsEvent() : - SupplicantEvent(SupplicantEvent::EVENT_SCAN_RESULTS, -1) { -} - -SupplicantScanResultsEvent::~SupplicantScanResultsEvent() { -} - diff --git a/nexus/SupplicantScanResultsEvent.h b/nexus/SupplicantScanResultsEvent.h deleted file mode 100644 index 5f82041..0000000 --- a/nexus/SupplicantScanResultsEvent.h +++ /dev/null @@ -1,30 +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. - */ - -#ifndef _SupplicantScanResultsEvent_H -#define _SupplicantScanResultsEvent_H - -#include "SupplicantEvent.h" - -class SupplicantScanResultsEvent : public SupplicantEvent { - -public: - SupplicantScanResultsEvent(int level, char *event, size_t len); - SupplicantScanResultsEvent(); - virtual ~SupplicantScanResultsEvent(); -}; - -#endif diff --git a/nexus/SupplicantState.cpp b/nexus/SupplicantState.cpp deleted file mode 100644 index 2815430..0000000 --- a/nexus/SupplicantState.cpp +++ /dev/null @@ -1,49 +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. - */ - -#include <stdio.h> - -#define LOG_TAG "SupplicantState" -#include <cutils/log.h> - -#include "SupplicantState.h" - -char *SupplicantState::toString(int val, char *buffer, int max) { - if (val == SupplicantState::UNKNOWN) - strncpy(buffer, "UNKNOWN", max); - else if (val == SupplicantState::DISCONNECTED) - strncpy(buffer, "DISCONNECTED", max); - else if (val == SupplicantState::INACTIVE) - strncpy(buffer, "INACTIVE", max); - else if (val == SupplicantState::SCANNING) - strncpy(buffer, "SCANNING", max); - else if (val == SupplicantState::ASSOCIATING) - strncpy(buffer, "ASSOCIATING", max); - else if (val == SupplicantState::ASSOCIATED) - strncpy(buffer, "ASSOCIATED", max); - else if (val == SupplicantState::FOURWAY_HANDSHAKE) - strncpy(buffer, "FOURWAY_HANDSHAKE", max); - else if (val == SupplicantState::GROUP_HANDSHAKE) - strncpy(buffer, "GROUP_HANDSHAKE", max); - else if (val == SupplicantState::COMPLETED) - strncpy(buffer, "COMPLETED", max); - else if (val == SupplicantState::IDLE) - strncpy(buffer, "IDLE", max); - else - strncpy(buffer, "(internal error)", max); - - return buffer; -} diff --git a/nexus/SupplicantState.h b/nexus/SupplicantState.h deleted file mode 100644 index 6882f0c..0000000 --- a/nexus/SupplicantState.h +++ /dev/null @@ -1,36 +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. - */ - -#ifndef _SUPPLICANT_STATE_H -#define _SUPPLICANT_STATE_H - -class SupplicantState { -public: - static const int UNKNOWN = -1; - static const int DISCONNECTED = 0; - static const int INACTIVE = 1; - static const int SCANNING = 2; - static const int ASSOCIATING = 3; - static const int ASSOCIATED = 4; - static const int FOURWAY_HANDSHAKE = 5; - static const int GROUP_HANDSHAKE = 6; - static const int COMPLETED = 7; - static const int IDLE = 8; - - static char *toString(int val, char *buffer, int max); -}; - -#endif diff --git a/nexus/SupplicantStateChangeEvent.cpp b/nexus/SupplicantStateChangeEvent.cpp deleted file mode 100644 index fd9233a..0000000 --- a/nexus/SupplicantStateChangeEvent.cpp +++ /dev/null @@ -1,45 +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. - */ - -#include <stdlib.h> - -#define LOG_TAG "SupplicantStateChangeEvent" -#include <cutils/log.h> - -#include "SupplicantStateChangeEvent.h" - -SupplicantStateChangeEvent::SupplicantStateChangeEvent(int level, char *event, - size_t len) : - SupplicantEvent(SupplicantEvent::EVENT_STATE_CHANGE, - level) { - // XXX: move this stuff into a static creation method - char *p = index(event, ' '); - if (!p) { - ALOGW("Bad event '%s'\n", event); - return; - } - - mState = atoi(p + strlen("state=") + 1); -} - -SupplicantStateChangeEvent::SupplicantStateChangeEvent(int state) : - SupplicantEvent(SupplicantEvent::EVENT_STATE_CHANGE, -1) { - mState = state; -} - -SupplicantStateChangeEvent::~SupplicantStateChangeEvent() { -} - diff --git a/nexus/SupplicantStateChangeEvent.h b/nexus/SupplicantStateChangeEvent.h deleted file mode 100644 index 77bff65..0000000 --- a/nexus/SupplicantStateChangeEvent.h +++ /dev/null @@ -1,34 +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. - */ - -#ifndef _SupplicantStateChangeEvent_H -#define _SupplicantStateChangeEvent_H - -#include "SupplicantEvent.h" - -class SupplicantStateChangeEvent : public SupplicantEvent { -private: - int mState; - -public: - SupplicantStateChangeEvent(int level, char *event, size_t len); - SupplicantStateChangeEvent(int state); - virtual ~SupplicantStateChangeEvent(); - - int getState() { return mState; } -}; - -#endif diff --git a/nexus/SupplicantStatus.cpp b/nexus/SupplicantStatus.cpp deleted file mode 100644 index 8f28abe..0000000 --- a/nexus/SupplicantStatus.cpp +++ /dev/null @@ -1,91 +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. - */ - -#include <stdlib.h> -#include <string.h> - -#define LOG_TAG "SupplicantStatus" -#include <cutils/log.h> - -#include "SupplicantStatus.h" -#include "SupplicantState.h" - -SupplicantStatus::SupplicantStatus() { - mWpaState = SupplicantState::UNKNOWN; - mId = -1; - mBssid = NULL; - mSsid = NULL; -} - -SupplicantStatus::SupplicantStatus(int state, int id, char *bssid, char *ssid) : - mWpaState(state), mId(id), mBssid(bssid), mSsid(ssid) { - -LOGD("state %d, id %d, bssid %p, ssid %p\n", mWpaState, mId, mBssid, mSsid); -} - -SupplicantStatus::~SupplicantStatus() { - if (mBssid) - free(mBssid); - if (mSsid) - free(mSsid); -} - -SupplicantStatus *SupplicantStatus::createStatus(char *data, int len) { - char *bssid = NULL; - char *ssid = NULL; - int id = -1; - int state = SupplicantState::UNKNOWN; - - char *next = data; - char *line; - while((line = strsep(&next, "\n"))) { - char *line_next = line; - char *token = strsep(&line_next, "="); - char *value = strsep(&line_next, "="); - if (!strcmp(token, "bssid")) - bssid = strdup(value); - else if (!strcmp(token, "ssid")) - ssid = strdup(value); - else if (!strcmp(token, "id")) - id = atoi(value); - else if (!strcmp(token, "wpa_state")) { - if (!strcmp(value, "DISCONNECTED")) - state = SupplicantState::DISCONNECTED; - else if (!strcmp(value, "INACTIVE")) - state = SupplicantState::INACTIVE; - else if (!strcmp(value, "SCANNING")) - state = SupplicantState::SCANNING; - else if (!strcmp(value, "ASSOCIATING")) - state = SupplicantState::ASSOCIATING; - else if (!strcmp(value, "ASSOCIATED")) - state = SupplicantState::ASSOCIATED; - else if (!strcmp(value, "FOURWAY_HANDSHAKE")) - state = SupplicantState::FOURWAY_HANDSHAKE; - else if (!strcmp(value, "GROUP_HANDSHAKE")) - state = SupplicantState::GROUP_HANDSHAKE; - else if (!strcmp(value, "COMPLETED")) - state = SupplicantState::COMPLETED; - else if (!strcmp(value, "IDLE")) - state = SupplicantState::IDLE; - else - ALOGE("Unknown supplicant state '%s'", value); - } else - ALOGD("Ignoring unsupported status token '%s'", token); - } - - return new SupplicantStatus(state, id, bssid, ssid); - -} diff --git a/nexus/SupplicantStatus.h b/nexus/SupplicantStatus.h deleted file mode 100644 index ef01841..0000000 --- a/nexus/SupplicantStatus.h +++ /dev/null @@ -1,42 +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. - */ - -#ifndef _SupplicantStatus_H -#define _SupplicantStatus_H - -class SupplicantStatus { -private: - int mWpaState; - int mId; - char *mBssid; - char *mSsid; - -private: - SupplicantStatus(); - SupplicantStatus(int state, int id, char *bssid, char *ssid); - -public: - virtual ~SupplicantStatus(); - static SupplicantStatus *createStatus(char *data, int len); - - int getWpaState() { return mWpaState; } - int getId() { return mId; } - const char *getBssid() { return mBssid; } - const char *getSsid() { return mSsid; } - -}; - -#endif diff --git a/nexus/TiwlanEventListener.cpp b/nexus/TiwlanEventListener.cpp deleted file mode 100644 index fde3a44..0000000 --- a/nexus/TiwlanEventListener.cpp +++ /dev/null @@ -1,61 +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. - */ - -#include <errno.h> -#include <pthread.h> -#include <sys/types.h> -#include <sys/types.h> -#include <sys/socket.h> - -#define LOG_TAG "TiwlanEventListener" -#include <cutils/log.h> - -#include "TiwlanEventListener.h" - -TiwlanEventListener::TiwlanEventListener(int socket) : - SocketListener(socket, false) { -} - -bool TiwlanEventListener::onDataAvailable(SocketClient *cli) { - struct ipc_ev_data *data; - - if (!(data = (struct ipc_ev_data *) malloc(sizeof(struct ipc_ev_data)))) { - ALOGE("Failed to allocate packet (out of memory)"); - return true; - } - - if (recv(cli->getSocket(), data, sizeof(struct ipc_ev_data), 0) < 0) { - ALOGE("recv failed (%s)", strerror(errno)); - goto out; - } - - if (data->event_type == IPC_EVENT_LINK_SPEED) { - uint32_t *spd = (uint32_t *) data->buffer; - *spd /= 2; -// ALOGD("Link speed = %u MB/s", *spd); - } else if (data->event_type == IPC_EVENT_LOW_SNR) { - ALOGW("Low signal/noise ratio"); - } else if (data->event_type == IPC_EVENT_LOW_RSSI) { - ALOGW("Low RSSI"); - } else { -// ALOGD("Dropping unhandled driver event %d", data->event_type); - } - - // TODO: Tell WifiController about the event -out: - free(data); - return true; -} diff --git a/nexus/TiwlanEventListener.h b/nexus/TiwlanEventListener.h deleted file mode 100644 index 052d6b1..0000000 --- a/nexus/TiwlanEventListener.h +++ /dev/null @@ -1,55 +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. - */ - -#ifndef _TIWLAN_EVENT_LISTENER_H__ -#define _TIWLAN_EVENT_LISTENER_H__ - -#include <sysutils/SocketListener.h> - -struct wpa_ctrl; -class SocketClient; -class ITiwlanEventHandler; -class TiwlanEventFactory; - -class TiwlanEventListener: public SocketListener { - -public: - TiwlanEventListener(int sock); - virtual ~TiwlanEventListener() {} - -protected: - virtual bool onDataAvailable(SocketClient *c); -}; - -// TODO: Move all this crap into a factory -#define TI_DRIVER_MSG_PORT 9001 - -#define IPC_EVENT_LINK_SPEED 2 -#define IPC_EVENT_LOW_SNR 13 -#define IPC_EVENT_LOW_RSSI 14 - -struct ipc_ev_data { - uint32_t event_type; - void *event_id; - uint32_t process_id; - uint32_t delivery_type; - uint32_t user_param; - void *event_callback; - uint32_t bufferSize; - uint8_t buffer[2048]; -}; - -#endif diff --git a/nexus/TiwlanWifiController.cpp b/nexus/TiwlanWifiController.cpp deleted file mode 100644 index 76b6a2e..0000000 --- a/nexus/TiwlanWifiController.cpp +++ /dev/null @@ -1,147 +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. - */ - -#include <stdlib.h> -#include <fcntl.h> -#include <errno.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> - -#include <cutils/properties.h> -#define LOG_TAG "TiwlanWifiController" -#include <cutils/log.h> - -#include "PropertyManager.h" -#include "TiwlanWifiController.h" -#include "TiwlanEventListener.h" - -#define DRIVER_PROP_NAME "wlan.driver.status" - -extern "C" int sched_yield(void); - -TiwlanWifiController::TiwlanWifiController(PropertyManager *propmngr, - IControllerHandler *handlers, - char *modpath, char *modname, - char *modargs) : - WifiController(propmngr, handlers, modpath, modname, - modargs) { - mEventListener = NULL; - mListenerSock = -1; -} - -int TiwlanWifiController::powerUp() { - return 0; // Powerup is currently done when the driver is loaded -} - -int TiwlanWifiController::powerDown() { - if (mEventListener) { - delete mEventListener; - shutdown(mListenerSock, SHUT_RDWR); - close(mListenerSock); - mListenerSock = -1; - mEventListener = NULL; - } - - return 0; // Powerdown is currently done when the driver is unloaded -} - -bool TiwlanWifiController::isPoweredUp() { - return isKernelModuleLoaded(getModuleName()); -} - -int TiwlanWifiController::loadFirmware() { - char driver_status[PROPERTY_VALUE_MAX]; - int count = 100; - - property_set("ctl.start", "wlan_loader"); - sched_yield(); - - // Wait for driver to be ready - while (count-- > 0) { - if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) { - if (!strcmp(driver_status, "ok")) { - ALOGD("Firmware loaded OK"); - - if (startDriverEventListener()) { - ALOGW("Failed to start driver event listener (%s)", - strerror(errno)); - } - - return 0; - } else if (!strcmp(DRIVER_PROP_NAME, "failed")) { - ALOGE("Firmware load failed"); - return -1; - } - } - usleep(200000); - } - property_set(DRIVER_PROP_NAME, "timeout"); - ALOGE("Firmware load timed out"); - return -1; -} - -int TiwlanWifiController::startDriverEventListener() { - struct sockaddr_in addr; - - if (mListenerSock != -1) { - ALOGE("Listener already started!"); - errno = EBUSY; - return -1; - } - - if ((mListenerSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - ALOGE("socket failed (%s)", strerror(errno)); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(TI_DRIVER_MSG_PORT); - - if (bind(mListenerSock, - (struct sockaddr *) &addr, - sizeof(addr)) < 0) { - ALOGE("bind failed (%s)", strerror(errno)); - goto out_err; - } - - mEventListener = new TiwlanEventListener(mListenerSock); - - if (mEventListener->startListener()) { - ALOGE("Error starting driver listener (%s)", strerror(errno)); - goto out_err; - } - return 0; -out_err: - if (mEventListener) { - delete mEventListener; - mEventListener = NULL; - } - if (mListenerSock != -1) { - shutdown(mListenerSock, SHUT_RDWR); - close(mListenerSock); - mListenerSock = -1; - } - return -1; -} - -bool TiwlanWifiController::isFirmwareLoaded() { - // Always load the firmware - return false; -} diff --git a/nexus/TiwlanWifiController.h b/nexus/TiwlanWifiController.h deleted file mode 100644 index 583be71..0000000 --- a/nexus/TiwlanWifiController.h +++ /dev/null @@ -1,43 +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. - */ - -#ifndef _TIWLAN_WIFI_CONTROLLER_H -#define _TIWLAN_WIFI_CONTROLLER_H - -#include "PropertyManager.h" -#include "WifiController.h" - -class IControllerHandler; -class TiwlanEventListener; - -class TiwlanWifiController : public WifiController { - int mListenerSock; - TiwlanEventListener *mEventListener; - -public: - TiwlanWifiController(PropertyManager *propmngr, IControllerHandler *handlers, char *modpath, char *modname, char *modargs); - virtual ~TiwlanWifiController() {} - - virtual int powerUp(); - virtual int powerDown(); - virtual bool isPoweredUp(); - virtual int loadFirmware(); - virtual bool isFirmwareLoaded(); - -private: - int startDriverEventListener(); -}; -#endif diff --git a/nexus/VpnController.cpp b/nexus/VpnController.cpp deleted file mode 100644 index 015710f..0000000 --- a/nexus/VpnController.cpp +++ /dev/null @@ -1,92 +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. - */ - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <errno.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include "PropertyManager.h" -#include "VpnController.h" - -VpnController::VpnController(PropertyManager *propmngr, - IControllerHandler *handlers) : - Controller("vpn", propmngr, handlers) { - mEnabled = false; - - mStaticProperties.propEnabled = new VpnEnabledProperty(this); - mDynamicProperties.propGateway = new IPV4AddressPropertyHelper("Gateway", - false, - &mGateway); -} - -int VpnController::start() { - mPropMngr->attachProperty("vpn", mStaticProperties.propEnabled); - return 0; -} - -int VpnController::stop() { - mPropMngr->detachProperty("vpn", mStaticProperties.propEnabled); - return 0; -} - -VpnController::VpnIntegerProperty::VpnIntegerProperty(VpnController *c, - const char *name, - bool ro, - int elements) : - IntegerProperty(name, ro, elements) { - mVc = c; -} - -VpnController::VpnStringProperty::VpnStringProperty(VpnController *c, - const char *name, - bool ro, int elements) : - StringProperty(name, ro, elements) { - mVc = c; -} - -VpnController::VpnIPV4AddressProperty::VpnIPV4AddressProperty(VpnController *c, - const char *name, - bool ro, int elements) : - IPV4AddressProperty(name, ro, elements) { - mVc = c; -} - -VpnController::VpnEnabledProperty::VpnEnabledProperty(VpnController *c) : - VpnIntegerProperty(c, "Enabled", false, 1) { -} -int VpnController::VpnEnabledProperty::get(int idx, int *buffer) { - *buffer = mVc->mEnabled; - return 0; -} -int VpnController::VpnEnabledProperty::set(int idx, int value) { - int rc; - if (!value) { - mVc->mPropMngr->detachProperty("vpn", mVc->mDynamicProperties.propGateway); - rc = mVc->disable(); - } else { - rc = mVc->enable(); - if (!rc) { - mVc->mPropMngr->attachProperty("vpn", mVc->mDynamicProperties.propGateway); - } - } - if (!rc) - mVc->mEnabled = value; - return rc; -} diff --git a/nexus/VpnController.h b/nexus/VpnController.h deleted file mode 100644 index 4bd86b5..0000000 --- a/nexus/VpnController.h +++ /dev/null @@ -1,97 +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. - */ - -#ifndef _VPN_CONTROLLER_H -#define _VPN_CONTROLLER_H - -#include <netinet/in.h> - -#include "Controller.h" - -class IControllerHandler; - -class VpnController : public Controller { - class VpnIntegerProperty : public IntegerProperty { - protected: - VpnController *mVc; - public: - VpnIntegerProperty(VpnController *c, const char *name, bool ro, - int elements); - virtual ~VpnIntegerProperty() {} - virtual int set(int idx, int value) = 0; - virtual int get(int idx, int *buffer) = 0; - }; - friend class VpnController::VpnIntegerProperty; - - class VpnStringProperty : public StringProperty { - protected: - VpnController *mVc; - public: - VpnStringProperty(VpnController *c, const char *name, bool ro, - int elements); - virtual ~VpnStringProperty() {} - virtual int set(int idx, const char *value) = 0; - virtual int get(int idx, char *buffer, size_t max) = 0; - }; - friend class VpnController::VpnStringProperty; - - class VpnIPV4AddressProperty : public IPV4AddressProperty { - protected: - VpnController *mVc; - public: - VpnIPV4AddressProperty(VpnController *c, const char *name, bool ro, - int elements); - virtual ~VpnIPV4AddressProperty() {} - virtual int set(int idx, struct in_addr *value) = 0; - virtual int get(int idx, struct in_addr *buffer) = 0; - }; - friend class VpnController::VpnIPV4AddressProperty; - - class VpnEnabledProperty : public VpnIntegerProperty { - public: - VpnEnabledProperty(VpnController *c); - virtual ~VpnEnabledProperty() {}; - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - bool mEnabled; - /* - * Gateway of the VPN server to connect to - */ - struct in_addr mGateway; - - struct { - VpnEnabledProperty *propEnabled; - } mStaticProperties; - - struct { - IPV4AddressPropertyHelper *propGateway; - } mDynamicProperties; - -public: - VpnController(PropertyManager *propmngr, IControllerHandler *handlers); - virtual ~VpnController() {} - - virtual int start(); - virtual int stop(); - -protected: - virtual int enable() = 0; - virtual int disable() = 0; -}; - -#endif diff --git a/nexus/WifiController.cpp b/nexus/WifiController.cpp deleted file mode 100644 index cedd013..0000000 --- a/nexus/WifiController.cpp +++ /dev/null @@ -1,819 +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. - */ - -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#define LOG_TAG "WifiController" -#include <cutils/log.h> - -#include "Supplicant.h" -#include "WifiController.h" -#include "NetworkManager.h" -#include "ResponseCode.h" -#include "WifiNetwork.h" -#include "ISupplicantEventHandler.h" -#include "SupplicantState.h" -#include "SupplicantStatus.h" -#include "SupplicantAssociatingEvent.h" -#include "SupplicantAssociatedEvent.h" -#include "SupplicantConnectedEvent.h" -#include "SupplicantScanResultsEvent.h" -#include "SupplicantStateChangeEvent.h" -#include "SupplicantConnectionTimeoutEvent.h" -#include "SupplicantDisconnectedEvent.h" -#include "WifiStatusPoller.h" - -WifiController::WifiController(PropertyManager *mPropMngr, - IControllerHandler *handlers, - char *modpath, char *modname, char *modargs) : - Controller("wifi", mPropMngr, handlers) { - strncpy(mModulePath, modpath, sizeof(mModulePath)); - strncpy(mModuleName, modname, sizeof(mModuleName)); - strncpy(mModuleArgs, modargs, sizeof(mModuleArgs)); - - mLatestScanResults = new ScanResultCollection(); - pthread_mutex_init(&mLatestScanResultsLock, NULL); - - pthread_mutex_init(&mLock, NULL); - - mSupplicant = new Supplicant(this, this); - mActiveScan = false; - mEnabled = false; - mScanOnly = false; - mPacketFilter = false; - mBluetoothCoexScan = false; - mBluetoothCoexMode = 0; - mCurrentlyConnectedNetworkId = -1; - mStatusPoller = new WifiStatusPoller(this); - mRssiEventThreshold = 5; - mLastLinkSpeed = 0; - - mSupplicantState = SupplicantState::UNKNOWN; - - mStaticProperties.propEnabled = new WifiEnabledProperty(this); - mStaticProperties.propScanOnly = new WifiScanOnlyProperty(this); - mStaticProperties.propAllowedChannels = new WifiAllowedChannelsProperty(this); - - mStaticProperties.propRssiEventThreshold = - new IntegerPropertyHelper("RssiEventThreshold", false, &mRssiEventThreshold); - - mDynamicProperties.propSupplicantState = new WifiSupplicantStateProperty(this); - mDynamicProperties.propActiveScan = new WifiActiveScanProperty(this); - mDynamicProperties.propInterface = new WifiInterfaceProperty(this); - mDynamicProperties.propSearching = new WifiSearchingProperty(this); - mDynamicProperties.propPacketFilter = new WifiPacketFilterProperty(this); - mDynamicProperties.propBluetoothCoexScan = new WifiBluetoothCoexScanProperty(this); - mDynamicProperties.propBluetoothCoexMode = new WifiBluetoothCoexModeProperty(this); - mDynamicProperties.propCurrentNetwork = new WifiCurrentNetworkProperty(this); - - mDynamicProperties.propRssi = new IntegerPropertyHelper("Rssi", true, &mLastRssi); - mDynamicProperties.propLinkSpeed = new IntegerPropertyHelper("LinkSpeed", true, &mLastLinkSpeed); - - mDynamicProperties.propSuspended = new WifiSuspendedProperty(this); - mDynamicProperties.propNetCount = new WifiNetCountProperty(this); - mDynamicProperties.propTriggerScan = new WifiTriggerScanProperty(this); -} - -int WifiController::start() { - mPropMngr->attachProperty("wifi", mStaticProperties.propEnabled); - mPropMngr->attachProperty("wifi", mStaticProperties.propScanOnly); - mPropMngr->attachProperty("wifi", mStaticProperties.propAllowedChannels); - mPropMngr->attachProperty("wifi", mStaticProperties.propRssiEventThreshold); - return 0; -} - -int WifiController::stop() { - mPropMngr->detachProperty("wifi", mStaticProperties.propEnabled); - mPropMngr->detachProperty("wifi", mStaticProperties.propScanOnly); - mPropMngr->detachProperty("wifi", mStaticProperties.propAllowedChannels); - mPropMngr->detachProperty("wifi", mStaticProperties.propRssiEventThreshold); - return 0; -} - -int WifiController::enable() { - - if (!isPoweredUp()) { - ALOGI("Powering up"); - sendStatusBroadcast("Powering up WiFi hardware"); - if (powerUp()) { - ALOGE("Powerup failed (%s)", strerror(errno)); - return -1; - } - } - - if (mModuleName[0] != '\0' && !isKernelModuleLoaded(mModuleName)) { - ALOGI("Loading driver"); - sendStatusBroadcast("Loading WiFi driver"); - if (loadKernelModule(mModulePath, mModuleArgs)) { - ALOGE("Kernel module load failed (%s)", strerror(errno)); - goto out_powerdown; - } - } - - if (!isFirmwareLoaded()) { - ALOGI("Loading firmware"); - sendStatusBroadcast("Loading WiFI firmware"); - if (loadFirmware()) { - ALOGE("Firmware load failed (%s)", strerror(errno)); - goto out_powerdown; - } - } - - if (!mSupplicant->isStarted()) { - ALOGI("Starting WPA Supplicant"); - sendStatusBroadcast("Starting WPA Supplicant"); - if (mSupplicant->start()) { - ALOGE("Supplicant start failed (%s)", strerror(errno)); - goto out_unloadmodule; - } - } - - if (Controller::bindInterface(mSupplicant->getInterfaceName())) { - ALOGE("Error binding interface (%s)", strerror(errno)); - goto out_unloadmodule; - } - - if (mSupplicant->refreshNetworkList()) - ALOGW("Error getting list of networks (%s)", strerror(errno)); - - ALOGW("TODO: Set # of allowed regulatory channels!"); - - mPropMngr->attachProperty("wifi", mDynamicProperties.propSupplicantState); - mPropMngr->attachProperty("wifi", mDynamicProperties.propActiveScan); - mPropMngr->attachProperty("wifi", mDynamicProperties.propInterface); - mPropMngr->attachProperty("wifi", mDynamicProperties.propSearching); - mPropMngr->attachProperty("wifi", mDynamicProperties.propPacketFilter); - mPropMngr->attachProperty("wifi", mDynamicProperties.propBluetoothCoexScan); - mPropMngr->attachProperty("wifi", mDynamicProperties.propBluetoothCoexMode); - mPropMngr->attachProperty("wifi", mDynamicProperties.propCurrentNetwork); - mPropMngr->attachProperty("wifi", mDynamicProperties.propRssi); - mPropMngr->attachProperty("wifi", mDynamicProperties.propLinkSpeed); - mPropMngr->attachProperty("wifi", mDynamicProperties.propSuspended); - mPropMngr->attachProperty("wifi", mDynamicProperties.propNetCount); - mPropMngr->attachProperty("wifi", mDynamicProperties.propTriggerScan); - - ALOGI("Enabled successfully"); - return 0; - -out_unloadmodule: - if (mModuleName[0] != '\0' && !isKernelModuleLoaded(mModuleName)) { - if (unloadKernelModule(mModuleName)) { - ALOGE("Unable to unload module after failure!"); - } - } - -out_powerdown: - if (powerDown()) { - ALOGE("Unable to powerdown after failure!"); - } - return -1; -} - -bool WifiController::getSuspended() { - pthread_mutex_lock(&mLock); - bool r = mSuspended; - pthread_mutex_unlock(&mLock); - return r; -} - -int WifiController::setSuspend(bool suspend) { - - pthread_mutex_lock(&mLock); - if (suspend == mSuspended) { - ALOGW("Suspended state already = %d", suspend); - pthread_mutex_unlock(&mLock); - return 0; - } - - if (suspend) { - mHandlers->onControllerSuspending(this); - - char tmp[80]; - ALOGD("Suspending from supplicant state %s", - SupplicantState::toString(mSupplicantState, - tmp, - sizeof(tmp))); - - if (mSupplicantState != SupplicantState::IDLE) { - ALOGD("Forcing Supplicant disconnect"); - if (mSupplicant->disconnect()) { - ALOGW("Error disconnecting (%s)", strerror(errno)); - } - } - - ALOGD("Stopping Supplicant driver"); - if (mSupplicant->stopDriver()) { - ALOGE("Error stopping driver (%s)", strerror(errno)); - pthread_mutex_unlock(&mLock); - return -1; - } - } else { - ALOGD("Resuming"); - - if (mSupplicant->startDriver()) { - ALOGE("Error resuming driver (%s)", strerror(errno)); - pthread_mutex_unlock(&mLock); - return -1; - } - // XXX: set regulatory max channels - if (mScanOnly) - mSupplicant->triggerScan(); - else - mSupplicant->reconnect(); - - mHandlers->onControllerResumed(this); - } - - mSuspended = suspend; - pthread_mutex_unlock(&mLock); - ALOGD("Suspend / Resume completed"); - return 0; -} - -void WifiController::sendStatusBroadcast(const char *msg) { - NetworkManager::Instance()-> - getBroadcaster()-> - sendBroadcast(ResponseCode::UnsolicitedInformational, msg, false); -} - -int WifiController::disable() { - - mPropMngr->detachProperty("wifi", mDynamicProperties.propSupplicantState); - mPropMngr->detachProperty("wifi", mDynamicProperties.propActiveScan); - mPropMngr->detachProperty("wifi", mDynamicProperties.propInterface); - mPropMngr->detachProperty("wifi", mDynamicProperties.propSearching); - mPropMngr->detachProperty("wifi", mDynamicProperties.propPacketFilter); - mPropMngr->detachProperty("wifi", mDynamicProperties.propBluetoothCoexScan); - mPropMngr->detachProperty("wifi", mDynamicProperties.propBluetoothCoexMode); - mPropMngr->detachProperty("wifi", mDynamicProperties.propCurrentNetwork); - mPropMngr->detachProperty("wifi", mDynamicProperties.propRssi); - mPropMngr->detachProperty("wifi", mDynamicProperties.propLinkSpeed); - mPropMngr->detachProperty("wifi", mDynamicProperties.propSuspended); - mPropMngr->detachProperty("wifi", mDynamicProperties.propNetCount); - - if (mSupplicant->isStarted()) { - sendStatusBroadcast("Stopping WPA Supplicant"); - if (mSupplicant->stop()) { - ALOGE("Supplicant stop failed (%s)", strerror(errno)); - return -1; - } - } else - ALOGW("disable(): Supplicant not running?"); - - if (mModuleName[0] != '\0' && isKernelModuleLoaded(mModuleName)) { - sendStatusBroadcast("Unloading WiFi driver"); - if (unloadKernelModule(mModuleName)) { - ALOGE("Unable to unload module (%s)", strerror(errno)); - return -1; - } - } - - if (isPoweredUp()) { - sendStatusBroadcast("Powering down WiFi hardware"); - if (powerDown()) { - ALOGE("Powerdown failed (%s)", strerror(errno)); - return -1; - } - } - return 0; -} - -int WifiController::loadFirmware() { - return 0; -} - -int WifiController::triggerScan() { - pthread_mutex_lock(&mLock); - if (verifyNotSuspended()) { - pthread_mutex_unlock(&mLock); - return -1; - } - - switch (mSupplicantState) { - case SupplicantState::DISCONNECTED: - case SupplicantState::INACTIVE: - case SupplicantState::SCANNING: - case SupplicantState::IDLE: - break; - default: - // Switch to scan only mode - mSupplicant->setApScanMode(2); - break; - } - - int rc = mSupplicant->triggerScan(); - pthread_mutex_unlock(&mLock); - return rc; -} - -int WifiController::setActiveScan(bool active) { - pthread_mutex_lock(&mLock); - if (mActiveScan == active) { - pthread_mutex_unlock(&mLock); - return 0; - } - mActiveScan = active; - - int rc = mSupplicant->setScanMode(active); - pthread_mutex_unlock(&mLock); - return rc; -} - -WifiNetwork *WifiController::createNetwork() { - pthread_mutex_lock(&mLock); - WifiNetwork *wn = mSupplicant->createNetwork(); - pthread_mutex_unlock(&mLock); - return wn; -} - -int WifiController::removeNetwork(int networkId) { - pthread_mutex_lock(&mLock); - WifiNetwork *wn = mSupplicant->lookupNetwork(networkId); - - if (!wn) { - pthread_mutex_unlock(&mLock); - return -1; - } - int rc = mSupplicant->removeNetwork(wn); - pthread_mutex_unlock(&mLock); - return rc; -} - -ScanResultCollection *WifiController::createScanResults() { - ScanResultCollection *d = new ScanResultCollection(); - ScanResultCollection::iterator i; - - pthread_mutex_lock(&mLatestScanResultsLock); - for (i = mLatestScanResults->begin(); i != mLatestScanResults->end(); ++i) - d->push_back((*i)->clone()); - - pthread_mutex_unlock(&mLatestScanResultsLock); - return d; -} - -WifiNetworkCollection *WifiController::createNetworkList() { - return mSupplicant->createNetworkList(); -} - -int WifiController::setPacketFilter(bool enable) { - int rc; - - pthread_mutex_lock(&mLock); - if (enable) - rc = mSupplicant->enablePacketFilter(); - else - rc = mSupplicant->disablePacketFilter(); - - if (!rc) - mPacketFilter = enable; - pthread_mutex_unlock(&mLock); - return rc; -} - -int WifiController::setBluetoothCoexistenceScan(bool enable) { - int rc; - - pthread_mutex_lock(&mLock); - - if (enable) - rc = mSupplicant->enableBluetoothCoexistenceScan(); - else - rc = mSupplicant->disableBluetoothCoexistenceScan(); - - if (!rc) - mBluetoothCoexScan = enable; - pthread_mutex_unlock(&mLock); - return rc; -} - -int WifiController::setScanOnly(bool scanOnly) { - pthread_mutex_lock(&mLock); - int rc = mSupplicant->setApScanMode((scanOnly ? 2 : 1)); - if (!rc) - mScanOnly = scanOnly; - if (!mSuspended) { - if (scanOnly) - mSupplicant->disconnect(); - else - mSupplicant->reconnect(); - } - pthread_mutex_unlock(&mLock); - return rc; -} - -int WifiController::setBluetoothCoexistenceMode(int mode) { - pthread_mutex_lock(&mLock); - int rc = mSupplicant->setBluetoothCoexistenceMode(mode); - if (!rc) - mBluetoothCoexMode = mode; - pthread_mutex_unlock(&mLock); - return rc; -} - -void WifiController::onAssociatingEvent(SupplicantAssociatingEvent *evt) { - ALOGD("onAssociatingEvent(%s, %s, %d)", - (evt->getBssid() ? evt->getBssid() : "n/a"), - (evt->getSsid() ? evt->getSsid() : "n/a"), - evt->getFreq()); -} - -void WifiController::onAssociatedEvent(SupplicantAssociatedEvent *evt) { - ALOGD("onAssociatedEvent(%s)", evt->getBssid()); -} - -void WifiController::onConnectedEvent(SupplicantConnectedEvent *evt) { - ALOGD("onConnectedEvent(%s, %d)", evt->getBssid(), evt->getReassociated()); - SupplicantStatus *ss = mSupplicant->getStatus(); - WifiNetwork *wn; - - if (ss->getWpaState() != SupplicantState::COMPLETED) { - char tmp[32]; - - ALOGW("onConnected() with SupplicantState = %s!", - SupplicantState::toString(ss->getWpaState(), tmp, - sizeof(tmp))); - return; - } - - if (ss->getId() == -1) { - ALOGW("onConnected() with id = -1!"); - return; - } - - mCurrentlyConnectedNetworkId = ss->getId(); - if (!(wn = mSupplicant->lookupNetwork(ss->getId()))) { - ALOGW("Error looking up connected network id %d (%s)", - ss->getId(), strerror(errno)); - return; - } - - delete ss; - mHandlers->onInterfaceConnected(this); -} - -void WifiController::onScanResultsEvent(SupplicantScanResultsEvent *evt) { - char *reply; - - if (!(reply = (char *) malloc(4096))) { - ALOGE("Out of memory"); - return; - } - - mNumScanResultsSinceLastStateChange++; - if (mNumScanResultsSinceLastStateChange >= 3) - mIsSupplicantSearching = false; - - size_t len = 4096; - - if (mSupplicant->sendCommand("SCAN_RESULTS", reply, &len)) { - ALOGW("onScanResultsEvent: Error getting scan results (%s)", - strerror(errno)); - free(reply); - return; - } - - pthread_mutex_lock(&mLatestScanResultsLock); - if (!mLatestScanResults->empty()) { - ScanResultCollection::iterator i; - - for (i = mLatestScanResults->begin(); - i !=mLatestScanResults->end(); ++i) { - delete *i; - } - mLatestScanResults->clear(); - } - - char *linep; - char *linep_next = NULL; - - if (!strtok_r(reply, "\n", &linep_next)) { - free(reply); - pthread_mutex_unlock(&mLatestScanResultsLock); - return; - } - - while((linep = strtok_r(NULL, "\n", &linep_next))) - mLatestScanResults->push_back(new ScanResult(linep)); - - // Switch handling of scan results back to normal mode - mSupplicant->setApScanMode(1); - - char *tmp; - asprintf(&tmp, "Scan results ready (%d)", mLatestScanResults->size()); - NetworkManager::Instance()->getBroadcaster()-> - sendBroadcast(ResponseCode::ScanResultsReady, - tmp, false); - free(tmp); - pthread_mutex_unlock(&mLatestScanResultsLock); - free(reply); -} - -void WifiController::onStateChangeEvent(SupplicantStateChangeEvent *evt) { - char tmp[32]; - char tmp2[32]; - - if (evt->getState() == mSupplicantState) - return; - - ALOGD("onStateChangeEvent(%s -> %s)", - SupplicantState::toString(mSupplicantState, tmp, sizeof(tmp)), - SupplicantState::toString(evt->getState(), tmp2, sizeof(tmp2))); - - if (evt->getState() != SupplicantState::SCANNING) { - mIsSupplicantSearching = true; - mNumScanResultsSinceLastStateChange = 0; - } - - char *tmp3; - asprintf(&tmp3, - "Supplicant state changed from %d (%s) -> %d (%s)", - mSupplicantState, tmp, evt->getState(), tmp2); - - mSupplicantState = evt->getState(); - - if (mSupplicantState == SupplicantState::COMPLETED) { - mStatusPoller->start(); - } else if (mStatusPoller->isStarted()) { - mStatusPoller->stop(); - } - - NetworkManager::Instance()->getBroadcaster()-> - sendBroadcast(ResponseCode::SupplicantStateChange, - tmp3, false); - free(tmp3); -} - -void WifiController::onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt) { - ALOGD("onConnectionTimeoutEvent(%s)", evt->getBssid()); -} - -void WifiController::onDisconnectedEvent(SupplicantDisconnectedEvent *evt) { - mCurrentlyConnectedNetworkId = -1; - mHandlers->onInterfaceDisconnected(this); -} - -#if 0 -void WifiController::onTerminatingEvent(SupplicantEvent *evt) { - ALOGD("onTerminatingEvent(%s)", evt->getEvent()); -} - -void WifiController::onPasswordChangedEvent(SupplicantEvent *evt) { - ALOGD("onPasswordChangedEvent(%s)", evt->getEvent()); -} - -void WifiController::onEapNotificationEvent(SupplicantEvent *evt) { - ALOGD("onEapNotificationEvent(%s)", evt->getEvent()); -} - -void WifiController::onEapStartedEvent(SupplicantEvent *evt) { - ALOGD("onEapStartedEvent(%s)", evt->getEvent()); -} - -void WifiController::onEapMethodEvent(SupplicantEvent *evt) { - ALOGD("onEapMethodEvent(%s)", evt->getEvent()); -} - -void WifiController::onEapSuccessEvent(SupplicantEvent *evt) { - ALOGD("onEapSuccessEvent(%s)", evt->getEvent()); -} - -void WifiController::onEapFailureEvent(SupplicantEvent *evt) { - ALOGD("onEapFailureEvent(%s)", evt->getEvent()); -} - -void WifiController::onLinkSpeedEvent(SupplicantEvent *evt) { - ALOGD("onLinkSpeedEvent(%s)", evt->getEvent()); -} - -void WifiController::onDriverStateEvent(SupplicantEvent *evt) { - ALOGD("onDriverStateEvent(%s)", evt->getEvent()); -} -#endif - -void WifiController::onStatusPollInterval() { - pthread_mutex_lock(&mLock); - int rssi; - if (mSupplicant->getRssi(&rssi)) { - ALOGE("Failed to get rssi (%s)", strerror(errno)); - pthread_mutex_unlock(&mLock); - return; - } - - if (abs(mLastRssi - rssi) > mRssiEventThreshold) { - char *tmp3; - asprintf(&tmp3, "RSSI changed from %d -> %d", - mLastRssi, rssi); - mLastRssi = rssi; - NetworkManager::Instance()->getBroadcaster()-> - sendBroadcast(ResponseCode::RssiChange, - tmp3, false); - free(tmp3); - } - - int linkspeed = mSupplicant->getLinkSpeed(); - if (linkspeed != mLastLinkSpeed) { - char *tmp3; - asprintf(&tmp3, "Link speed changed from %d -> %d", - mLastLinkSpeed, linkspeed); - mLastLinkSpeed = linkspeed; - NetworkManager::Instance()->getBroadcaster()-> - sendBroadcast(ResponseCode::LinkSpeedChange, - tmp3, false); - free(tmp3); - - } - pthread_mutex_unlock(&mLock); -} - -int WifiController::verifyNotSuspended() { - if (mSuspended) { - errno = ESHUTDOWN; - return -1; - } - return 0; -} - -/* - * Property inner classes - */ - -WifiController::WifiIntegerProperty::WifiIntegerProperty(WifiController *c, - const char *name, - bool ro, - int elements) : - IntegerProperty(name, ro, elements) { - mWc = c; -} - -WifiController::WifiStringProperty::WifiStringProperty(WifiController *c, - const char *name, - bool ro, int elements) : - StringProperty(name, ro, elements) { - mWc = c; -} - -WifiController::WifiEnabledProperty::WifiEnabledProperty(WifiController *c) : - WifiIntegerProperty(c, "Enabled", false, 1) { -} - -int WifiController::WifiEnabledProperty::get(int idx, int *buffer) { - *buffer = mWc->mEnabled; - return 0; -} -int WifiController::WifiEnabledProperty::set(int idx, int value) { - int rc = (value ? mWc->enable() : mWc->disable()); - if (!rc) - mWc->mEnabled = value; - return rc; -} - -WifiController::WifiScanOnlyProperty::WifiScanOnlyProperty(WifiController *c) : - WifiIntegerProperty(c, "ScanOnly", false, 1) { -} -int WifiController::WifiScanOnlyProperty::get(int idx, int *buffer) { - *buffer = mWc->mScanOnly; - return 0; -} -int WifiController::WifiScanOnlyProperty::set(int idx, int value) { - return mWc->setScanOnly(value == 1); -} - -WifiController::WifiAllowedChannelsProperty::WifiAllowedChannelsProperty(WifiController *c) : - WifiIntegerProperty(c, "AllowedChannels", false, 1) { -} -int WifiController::WifiAllowedChannelsProperty::get(int idx, int *buffer) { - *buffer = mWc->mNumAllowedChannels; - return 0; -} -int WifiController::WifiAllowedChannelsProperty::set(int idx, int value) { - // XXX: IMPL - errno = ENOSYS; - return -1; -} - -WifiController::WifiSupplicantStateProperty::WifiSupplicantStateProperty(WifiController *c) : - WifiStringProperty(c, "SupplicantState", true, 1) { -} -int WifiController::WifiSupplicantStateProperty::get(int idx, char *buffer, size_t max) { - if (!SupplicantState::toString(mWc->mSupplicantState, buffer, max)) - return -1; - return 0; -} - -WifiController::WifiActiveScanProperty::WifiActiveScanProperty(WifiController *c) : - WifiIntegerProperty(c, "ActiveScan", false, 1) { -} -int WifiController::WifiActiveScanProperty::get(int idx, int *buffer) { - *buffer = mWc->mActiveScan; - return 0; -} -int WifiController::WifiActiveScanProperty::set(int idx, int value) { - return mWc->setActiveScan(value); -} - -WifiController::WifiInterfaceProperty::WifiInterfaceProperty(WifiController *c) : - WifiStringProperty(c, "Interface", true, 1) { -} -int WifiController::WifiInterfaceProperty::get(int idx, char *buffer, size_t max) { - strncpy(buffer, (mWc->getBoundInterface() ? mWc->getBoundInterface() : "none"), max); - return 0; -} - -WifiController::WifiSearchingProperty::WifiSearchingProperty(WifiController *c) : - WifiIntegerProperty(c, "Searching", true, 1) { -} -int WifiController::WifiSearchingProperty::get(int idx, int *buffer) { - *buffer = mWc->mIsSupplicantSearching; - return 0; -} - -WifiController::WifiPacketFilterProperty::WifiPacketFilterProperty(WifiController *c) : - WifiIntegerProperty(c, "PacketFilter", false, 1) { -} -int WifiController::WifiPacketFilterProperty::get(int idx, int *buffer) { - *buffer = mWc->mPacketFilter; - return 0; -} -int WifiController::WifiPacketFilterProperty::set(int idx, int value) { - return mWc->setPacketFilter(value); -} - -WifiController::WifiBluetoothCoexScanProperty::WifiBluetoothCoexScanProperty(WifiController *c) : - WifiIntegerProperty(c, "BluetoothCoexScan", false, 1) { -} -int WifiController::WifiBluetoothCoexScanProperty::get(int idx, int *buffer) { - *buffer = mWc->mBluetoothCoexScan; - return 0; -} -int WifiController::WifiBluetoothCoexScanProperty::set(int idx, int value) { - return mWc->setBluetoothCoexistenceScan(value == 1); -} - -WifiController::WifiBluetoothCoexModeProperty::WifiBluetoothCoexModeProperty(WifiController *c) : - WifiIntegerProperty(c, "BluetoothCoexMode", false, 1) { -} -int WifiController::WifiBluetoothCoexModeProperty::get(int idx, int *buffer) { - *buffer = mWc->mBluetoothCoexMode; - return 0; -} -int WifiController::WifiBluetoothCoexModeProperty::set(int idx, int value) { - return mWc->setBluetoothCoexistenceMode(value); -} - -WifiController::WifiCurrentNetworkProperty::WifiCurrentNetworkProperty(WifiController *c) : - WifiIntegerProperty(c, "CurrentlyConnectedNetworkId", true, 1) { -} -int WifiController::WifiCurrentNetworkProperty::get(int idx, int *buffer) { - *buffer = mWc->mCurrentlyConnectedNetworkId; - return 0; -} - -WifiController::WifiSuspendedProperty::WifiSuspendedProperty(WifiController *c) : - WifiIntegerProperty(c, "Suspended", false, 1) { -} -int WifiController::WifiSuspendedProperty::get(int idx, int *buffer) { - *buffer = mWc->getSuspended(); - return 0; -} -int WifiController::WifiSuspendedProperty::set(int idx, int value) { - return mWc->setSuspend(value == 1); -} - -WifiController::WifiNetCountProperty::WifiNetCountProperty(WifiController *c) : - WifiIntegerProperty(c, "NetCount", true, 1) { -} -int WifiController::WifiNetCountProperty::get(int idx, int *buffer) { - pthread_mutex_lock(&mWc->mLock); - *buffer = mWc->mSupplicant->getNetworkCount(); - pthread_mutex_unlock(&mWc->mLock); - return 0; -} - -WifiController::WifiTriggerScanProperty::WifiTriggerScanProperty(WifiController *c) : - WifiIntegerProperty(c, "TriggerScan", false, 1) { -} -int WifiController::WifiTriggerScanProperty::get(int idx, int *buffer) { - // XXX: Need action type - *buffer = 0; - return 0; -} - -int WifiController::WifiTriggerScanProperty::set(int idx, int value) { - return mWc->triggerScan(); -} - diff --git a/nexus/WifiController.h b/nexus/WifiController.h deleted file mode 100644 index b1524f6..0000000 --- a/nexus/WifiController.h +++ /dev/null @@ -1,296 +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. - */ - -#ifndef _WIFI_CONTROLLER_H -#define _WIFI_CONTROLLER_H - -#include <sys/types.h> - -#include "Controller.h" -#include "ScanResult.h" -#include "WifiNetwork.h" -#include "ISupplicantEventHandler.h" -#include "IWifiStatusPollerHandler.h" - -class NetInterface; -class Supplicant; -class SupplicantAssociatingEvent; -class SupplicantAssociatedEvent; -class SupplicantConnectedEvent; -class SupplicantScanResultsEvent; -class SupplicantStateChangeEvent; -class SupplicantDisconnectedEvent; -class WifiStatusPoller; - -class WifiController : public Controller, - public ISupplicantEventHandler, - public IWifiStatusPollerHandler { - - class WifiIntegerProperty : public IntegerProperty { - protected: - WifiController *mWc; - public: - WifiIntegerProperty(WifiController *c, const char *name, bool ro, - int elements); - virtual ~WifiIntegerProperty() {} - virtual int set(int idx, int value) = 0; - virtual int get(int idx, int *buffer) = 0; - }; - friend class WifiController::WifiIntegerProperty; - - class WifiStringProperty : public StringProperty { - protected: - WifiController *mWc; - public: - WifiStringProperty(WifiController *c, const char *name, bool ro, - int elements); - virtual ~WifiStringProperty() {} - virtual int set(int idx, const char *value) = 0; - virtual int get(int idx, char *buffer, size_t max) = 0; - }; - friend class WifiController::WifiStringProperty; - - class WifiEnabledProperty : public WifiIntegerProperty { - public: - WifiEnabledProperty(WifiController *c); - virtual ~WifiEnabledProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiScanOnlyProperty : public WifiIntegerProperty { - public: - WifiScanOnlyProperty(WifiController *c); - virtual ~WifiScanOnlyProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiAllowedChannelsProperty : public WifiIntegerProperty { - public: - WifiAllowedChannelsProperty(WifiController *c); - virtual ~WifiAllowedChannelsProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiActiveScanProperty : public WifiIntegerProperty { - public: - WifiActiveScanProperty(WifiController *c); - virtual ~WifiActiveScanProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiSearchingProperty : public WifiIntegerProperty { - public: - WifiSearchingProperty(WifiController *c); - virtual ~WifiSearchingProperty() {} - int set(int idx, int value) { return -1; } - int get(int idx, int *buffer); - }; - - class WifiPacketFilterProperty : public WifiIntegerProperty { - public: - WifiPacketFilterProperty(WifiController *c); - virtual ~WifiPacketFilterProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiBluetoothCoexScanProperty : public WifiIntegerProperty { - public: - WifiBluetoothCoexScanProperty(WifiController *c); - virtual ~WifiBluetoothCoexScanProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiBluetoothCoexModeProperty : public WifiIntegerProperty { - public: - WifiBluetoothCoexModeProperty(WifiController *c); - virtual ~WifiBluetoothCoexModeProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiCurrentNetworkProperty : public WifiIntegerProperty { - public: - WifiCurrentNetworkProperty(WifiController *c); - virtual ~WifiCurrentNetworkProperty() {} - int set(int idx, int value) { return -1; } - int get(int idx, int *buffer); - }; - - class WifiSuspendedProperty : public WifiIntegerProperty { - public: - WifiSuspendedProperty(WifiController *c); - virtual ~WifiSuspendedProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiNetCountProperty : public WifiIntegerProperty { - public: - WifiNetCountProperty(WifiController *c); - virtual ~WifiNetCountProperty() {} - int set(int idx, int value) { return -1; } - int get(int idx, int *buffer); - }; - - class WifiTriggerScanProperty : public WifiIntegerProperty { - public: - WifiTriggerScanProperty(WifiController *c); - virtual ~WifiTriggerScanProperty() {} - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiSupplicantStateProperty : public WifiStringProperty { - public: - WifiSupplicantStateProperty(WifiController *c); - virtual ~WifiSupplicantStateProperty() {} - int set(int idx, const char *value) { return -1; } - int get(int idx, char *buffer, size_t max); - }; - - class WifiInterfaceProperty : public WifiStringProperty { - public: - WifiInterfaceProperty(WifiController *c); - virtual ~WifiInterfaceProperty() {} - int set(int idx, const char *value) { return -1; } - int get(int idx, char *buffer, size_t max); - }; - - Supplicant *mSupplicant; - char mModulePath[255]; - char mModuleName[64]; - char mModuleArgs[255]; - - int mSupplicantState; - bool mActiveScan; - bool mScanOnly; - bool mPacketFilter; - bool mBluetoothCoexScan; - int mBluetoothCoexMode; - int mCurrentlyConnectedNetworkId; - bool mSuspended; - int mLastRssi; - int mRssiEventThreshold; - int mLastLinkSpeed; - int mNumAllowedChannels; - - ScanResultCollection *mLatestScanResults; - pthread_mutex_t mLatestScanResultsLock; - pthread_mutex_t mLock; - WifiStatusPoller *mStatusPoller; - - struct { - WifiEnabledProperty *propEnabled; - WifiScanOnlyProperty *propScanOnly; - WifiAllowedChannelsProperty *propAllowedChannels; - IntegerPropertyHelper *propRssiEventThreshold; - } mStaticProperties; - - struct { - WifiActiveScanProperty *propActiveScan; - WifiSearchingProperty *propSearching; - WifiPacketFilterProperty *propPacketFilter; - WifiBluetoothCoexScanProperty *propBluetoothCoexScan; - WifiBluetoothCoexModeProperty *propBluetoothCoexMode; - WifiCurrentNetworkProperty *propCurrentNetwork; - IntegerPropertyHelper *propRssi; - IntegerPropertyHelper *propLinkSpeed; - WifiSuspendedProperty *propSuspended; - WifiNetCountProperty *propNetCount; - WifiSupplicantStateProperty *propSupplicantState; - WifiInterfaceProperty *propInterface; - WifiTriggerScanProperty *propTriggerScan; - } mDynamicProperties; - - // True if supplicant is currently searching for a network - bool mIsSupplicantSearching; - int mNumScanResultsSinceLastStateChange; - - bool mEnabled; - -public: - WifiController(PropertyManager *propmngr, IControllerHandler *handlers, char *modpath, char *modname, char *modargs); - virtual ~WifiController() {} - - int start(); - int stop(); - - WifiNetwork *createNetwork(); - int removeNetwork(int networkId); - WifiNetworkCollection *createNetworkList(); - - ScanResultCollection *createScanResults(); - - char *getModulePath() { return mModulePath; } - char *getModuleName() { return mModuleName; } - char *getModuleArgs() { return mModuleArgs; } - - Supplicant *getSupplicant() { return mSupplicant; } - -protected: - // Move this crap into a 'driver' - virtual int powerUp() = 0; - virtual int powerDown() = 0; - virtual int loadFirmware(); - - virtual bool isFirmwareLoaded() = 0; - virtual bool isPoweredUp() = 0; - -private: - void sendStatusBroadcast(const char *msg); - int setActiveScan(bool active); - int triggerScan(); - int enable(); - int disable(); - int setSuspend(bool suspend); - bool getSuspended(); - int setBluetoothCoexistenceScan(bool enable); - int setBluetoothCoexistenceMode(int mode); - int setPacketFilter(bool enable); - int setScanOnly(bool scanOnly); - - // ISupplicantEventHandler methods - void onAssociatingEvent(SupplicantAssociatingEvent *evt); - void onAssociatedEvent(SupplicantAssociatedEvent *evt); - void onConnectedEvent(SupplicantConnectedEvent *evt); - void onScanResultsEvent(SupplicantScanResultsEvent *evt); - void onStateChangeEvent(SupplicantStateChangeEvent *evt); - void onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt); - void onDisconnectedEvent(SupplicantDisconnectedEvent *evt); -#if 0 - virtual void onTerminatingEvent(SupplicantEvent *evt); - virtual void onPasswordChangedEvent(SupplicantEvent *evt); - virtual void onEapNotificationEvent(SupplicantEvent *evt); - virtual void onEapStartedEvent(SupplicantEvent *evt); - virtual void onEapMethodEvent(SupplicantEvent *evt); - virtual void onEapSuccessEvent(SupplicantEvent *evt); - virtual void onEapFailureEvent(SupplicantEvent *evt); - virtual void onLinkSpeedEvent(SupplicantEvent *evt); - virtual void onDriverStateEvent(SupplicantEvent *evt); -#endif - - void onStatusPollInterval(); - - int verifyNotSuspended(); -}; - -#endif diff --git a/nexus/WifiNetwork.cpp b/nexus/WifiNetwork.cpp deleted file mode 100644 index 0197b64..0000000 --- a/nexus/WifiNetwork.cpp +++ /dev/null @@ -1,970 +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. - */ - -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <sys/types.h> - -#define LOG_TAG "WifiNetwork" -#include <cutils/log.h> - -#include "NetworkManager.h" -#include "WifiNetwork.h" -#include "Supplicant.h" -#include "WifiController.h" - -WifiNetwork::WifiNetwork() { - // This is private to restrict copy constructors -} - -WifiNetwork::WifiNetwork(WifiController *c, Supplicant *suppl, const char *data) { - mController = c; - mSuppl = suppl; - - char *tmp = strdup(data); - char *next = tmp; - char *id; - char *ssid; - char *bssid; - char *flags; - - if (!(id = strsep(&next, "\t"))) - ALOGE("Failed to extract network id"); - if (!(ssid = strsep(&next, "\t"))) - ALOGE("Failed to extract ssid"); - if (!(bssid = strsep(&next, "\t"))) - ALOGE("Failed to extract bssid"); - if (!(flags = strsep(&next, "\t"))) - ALOGE("Failed to extract flags"); - - // ALOGD("id '%s', ssid '%s', bssid '%s', flags '%s'", id, ssid, bssid, - // flags ? flags :"null"); - - if (id) - mNetid = atoi(id); - if (ssid) - mSsid = strdup(ssid); - if (bssid) - mBssid = strdup(bssid); - - mPsk = NULL; - memset(mWepKeys, 0, sizeof(mWepKeys)); - mDefaultKeyIndex = -1; - mPriority = -1; - mHiddenSsid = NULL; - mKeyManagement = KeyManagementMask::UNKNOWN; - mProtocols = 0; - mAuthAlgorithms = 0; - mPairwiseCiphers = 0; - mGroupCiphers = 0; - mEnabled = true; - - if (flags && flags[0] != '\0') { - if (!strcmp(flags, "[DISABLED]")) - mEnabled = false; - else - ALOGW("Unsupported flags '%s'", flags); - } - - free(tmp); - createProperties(); -} - -WifiNetwork::WifiNetwork(WifiController *c, Supplicant *suppl, int networkId) { - mController = c; - mSuppl = suppl; - mNetid = networkId; - mSsid = NULL; - mBssid = NULL; - mPsk = NULL; - memset(mWepKeys, 0, sizeof(mWepKeys)); - mDefaultKeyIndex = -1; - mPriority = -1; - mHiddenSsid = NULL; - mKeyManagement = 0; - mProtocols = 0; - mAuthAlgorithms = 0; - mPairwiseCiphers = 0; - mGroupCiphers = 0; - mEnabled = false; - createProperties(); -} - -WifiNetwork *WifiNetwork::clone() { - WifiNetwork *r = new WifiNetwork(); - - r->mSuppl = mSuppl; - r->mNetid = mNetid; - - if (mSsid) - r->mSsid = strdup(mSsid); - if (mBssid) - r->mBssid = strdup(mBssid); - if (mPsk) - r->mPsk = strdup(mPsk); - - r->mController = mController; - memcpy(r->mWepKeys, mWepKeys, sizeof(mWepKeys)); - r->mDefaultKeyIndex = mDefaultKeyIndex; - r->mPriority = mPriority; - if (mHiddenSsid) - r->mHiddenSsid = strdup(mHiddenSsid); - r->mKeyManagement = mKeyManagement; - r->mProtocols = mProtocols; - r->mAuthAlgorithms = mAuthAlgorithms; - r->mPairwiseCiphers = mPairwiseCiphers; - r->mGroupCiphers = mGroupCiphers; - return r; -} - -void WifiNetwork::createProperties() { - asprintf(&mPropNamespace, "wifi.net.%d", mNetid); - - mStaticProperties.propEnabled = new WifiNetworkEnabledProperty(this); - mStaticProperties.propSsid = new WifiNetworkSsidProperty(this); - mStaticProperties.propBssid = new WifiNetworkBssidProperty(this); - mStaticProperties.propPsk = new WifiNetworkPskProperty(this); - mStaticProperties.propWepKey = new WifiNetworkWepKeyProperty(this); - mStaticProperties.propDefKeyIdx = new WifiNetworkDefaultKeyIndexProperty(this); - mStaticProperties.propPriority = new WifiNetworkPriorityProperty(this); - mStaticProperties.propKeyManagement = new WifiNetworkKeyManagementProperty(this); - mStaticProperties.propProtocols = new WifiNetworkProtocolsProperty(this); - mStaticProperties.propAuthAlgorithms = new WifiNetworkAuthAlgorithmsProperty(this); - mStaticProperties.propPairwiseCiphers = new WifiNetworkPairwiseCiphersProperty(this); - mStaticProperties.propGroupCiphers = new WifiNetworkGroupCiphersProperty(this); - mStaticProperties.propHiddenSsid = new WifiNetworkHiddenSsidProperty(this); -} - -WifiNetwork::~WifiNetwork() { - if (mPropNamespace) - free(mPropNamespace); - if (mSsid) - free(mSsid); - if (mBssid) - free(mBssid); - if (mPsk) - free(mPsk); - for (int i = 0; i < 4; i++) { - if (mWepKeys[i]) - free(mWepKeys[i]); - } - - if (mHiddenSsid) - free(mHiddenSsid); - - delete mStaticProperties.propEnabled; - delete mStaticProperties.propSsid; - delete mStaticProperties.propBssid; - delete mStaticProperties.propPsk; - delete mStaticProperties.propWepKey; - delete mStaticProperties.propDefKeyIdx; - delete mStaticProperties.propPriority; - delete mStaticProperties.propKeyManagement; - delete mStaticProperties.propProtocols; - delete mStaticProperties.propAuthAlgorithms; - delete mStaticProperties.propPairwiseCiphers; - delete mStaticProperties.propGroupCiphers; - delete mStaticProperties.propHiddenSsid; -} - -int WifiNetwork::refresh() { - char buffer[255]; - size_t len; - uint32_t mask; - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "psk", buffer, len)) - mPsk = strdup(buffer); - - for (int i = 0; i < 4; i++) { - char *name; - - asprintf(&name, "wep_key%d", i); - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, name, buffer, len)) - mWepKeys[i] = strdup(buffer); - free(name); - } - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "wep_tx_keyidx", buffer, len)) - mDefaultKeyIndex = atoi(buffer); - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "priority", buffer, len)) - mPriority = atoi(buffer); - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "scan_ssid", buffer, len)) - mHiddenSsid = strdup(buffer); - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "key_mgmt", buffer, len)) { - if (WifiNetwork::parseKeyManagementMask(buffer, &mask)) { - ALOGE("Error parsing key_mgmt (%s)", strerror(errno)); - } else { - mKeyManagement = mask; - } - } - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "proto", buffer, len)) { - if (WifiNetwork::parseProtocolsMask(buffer, &mask)) { - ALOGE("Error parsing proto (%s)", strerror(errno)); - } else { - mProtocols = mask; - } - } - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "auth_alg", buffer, len)) { - if (WifiNetwork::parseAuthAlgorithmsMask(buffer, &mask)) { - ALOGE("Error parsing auth_alg (%s)", strerror(errno)); - } else { - mAuthAlgorithms = mask; - } - } - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "pairwise", buffer, len)) { - if (WifiNetwork::parsePairwiseCiphersMask(buffer, &mask)) { - ALOGE("Error parsing pairwise (%s)", strerror(errno)); - } else { - mPairwiseCiphers = mask; - } - } - - len = sizeof(buffer); - if (mSuppl->getNetworkVar(mNetid, "group", buffer, len)) { - if (WifiNetwork::parseGroupCiphersMask(buffer, &mask)) { - ALOGE("Error parsing group (%s)", strerror(errno)); - } else { - mGroupCiphers = mask; - } - } - - return 0; -out_err: - ALOGE("Refresh failed (%s)",strerror(errno)); - return -1; -} - -int WifiNetwork::setSsid(const char *ssid) { - char tmp[255]; - snprintf(tmp, sizeof(tmp), "\"%s\"", ssid); - if (mSuppl->setNetworkVar(mNetid, "ssid", tmp)) - return -1; - if (mSsid) - free(mSsid); - mSsid = strdup(ssid); - return 0; -} - -int WifiNetwork::setBssid(const char *bssid) { - if (mSuppl->setNetworkVar(mNetid, "bssid", bssid)) - return -1; - if (mBssid) - free(mBssid); - mBssid = strdup(bssid); - return 0; -} - -int WifiNetwork::setPsk(const char *psk) { - char tmp[255]; - snprintf(tmp, sizeof(tmp), "\"%s\"", psk); - if (mSuppl->setNetworkVar(mNetid, "psk", tmp)) - return -1; - - if (mPsk) - free(mPsk); - mPsk = strdup(psk); - return 0; -} - -int WifiNetwork::setWepKey(int idx, const char *key) { - char *name; - - asprintf(&name, "wep_key%d", idx); - int rc = mSuppl->setNetworkVar(mNetid, name, key); - free(name); - - if (rc) - return -1; - - if (mWepKeys[idx]) - free(mWepKeys[idx]); - mWepKeys[idx] = strdup(key); - return 0; -} - -int WifiNetwork::setDefaultKeyIndex(int idx) { - char val[16]; - sprintf(val, "%d", idx); - if (mSuppl->setNetworkVar(mNetid, "wep_tx_keyidx", val)) - return -1; - - mDefaultKeyIndex = idx; - return 0; -} - -int WifiNetwork::setPriority(int priority) { - char val[16]; - sprintf(val, "%d", priority); - if (mSuppl->setNetworkVar(mNetid, "priority", val)) - return -1; - - mPriority = priority; - return 0; -} - -int WifiNetwork::setHiddenSsid(const char *ssid) { - if (mSuppl->setNetworkVar(mNetid, "scan_ssid", ssid)) - return -1; - - if (mHiddenSsid) - free(mHiddenSsid); - mHiddenSsid = strdup(ssid); - return 0; -} - -int WifiNetwork::setKeyManagement(uint32_t mask) { - char accum[64] = {'\0'}; - - if (mask == KeyManagementMask::NONE) - strcpy(accum, "NONE"); - else { - if (mask & KeyManagementMask::WPA_PSK) - strcat(accum, "WPA-PSK"); - if (mask & KeyManagementMask::WPA_EAP) { - if (accum[0] != '\0') - strcat(accum, " "); - strcat(accum, "WPA-EAP"); - } - if (mask & KeyManagementMask::IEEE8021X) { - if (accum[0] != '\0') - strcat(accum, " "); - strcat(accum, "IEEE8021X"); - } - } - - if (mSuppl->setNetworkVar(mNetid, "key_mgmt", accum)) - return -1; - mKeyManagement = mask; - return 0; -} - -int WifiNetwork::setProtocols(uint32_t mask) { - char accum[64]; - - accum[0] = '\0'; - - if (mask & SecurityProtocolMask::WPA) - strcpy(accum, "WPA "); - - if (mask & SecurityProtocolMask::RSN) - strcat(accum, "RSN"); - - if (mSuppl->setNetworkVar(mNetid, "proto", accum)) - return -1; - mProtocols = mask; - return 0; -} - -int WifiNetwork::setAuthAlgorithms(uint32_t mask) { - char accum[64]; - - accum[0] = '\0'; - - if (mask == 0) - strcpy(accum, ""); - - if (mask & AuthenticationAlgorithmMask::OPEN) - strcpy(accum, "OPEN "); - - if (mask & AuthenticationAlgorithmMask::SHARED) - strcat(accum, "SHARED "); - - if (mask & AuthenticationAlgorithmMask::LEAP) - strcat(accum, "LEAP "); - - if (mSuppl->setNetworkVar(mNetid, "auth_alg", accum)) - return -1; - - mAuthAlgorithms = mask; - return 0; -} - -int WifiNetwork::setPairwiseCiphers(uint32_t mask) { - char accum[64]; - - accum[0] = '\0'; - - if (mask == PairwiseCiphersMask::NONE) - strcpy(accum, "NONE"); - else { - if (mask & PairwiseCiphersMask::TKIP) - strcat(accum, "TKIP "); - if (mask & PairwiseCiphersMask::CCMP) - strcat(accum, "CCMP "); - } - - if (mSuppl->setNetworkVar(mNetid, "pairwise", accum)) - return -1; - - mPairwiseCiphers = mask; - return 0; -} - -int WifiNetwork::setGroupCiphers(uint32_t mask) { - char accum[64]; - - accum[0] = '\0'; - - if (mask & GroupCiphersMask::WEP40) - strcat(accum, "WEP40 "); - if (mask & GroupCiphersMask::WEP104) - strcat(accum, "WEP104 "); - if (mask & GroupCiphersMask::TKIP) - strcat(accum, "TKIP "); - if (mask & GroupCiphersMask::CCMP) - strcat(accum, "CCMP "); - - if (mSuppl->setNetworkVar(mNetid, "group", accum)) - return -1; - mGroupCiphers = mask; - return 0; -} - -int WifiNetwork::setEnabled(bool enabled) { - - if (enabled) { - if (getPriority() == -1) { - ALOGE("Cannot enable network when priority is not set"); - errno = EAGAIN; - return -1; - } - if (getKeyManagement() == KeyManagementMask::UNKNOWN) { - ALOGE("Cannot enable network when KeyManagement is not set"); - errno = EAGAIN; - return -1; - } - } - - if (mSuppl->enableNetwork(mNetid, enabled)) - return -1; - - mEnabled = enabled; - return 0; -} - -int WifiNetwork::attachProperties(PropertyManager *pm, const char *nsName) { - pm->attachProperty(nsName, mStaticProperties.propSsid); - pm->attachProperty(nsName, mStaticProperties.propBssid); - pm->attachProperty(nsName, mStaticProperties.propPsk); - pm->attachProperty(nsName, mStaticProperties.propWepKey); - pm->attachProperty(nsName, mStaticProperties.propDefKeyIdx); - pm->attachProperty(nsName, mStaticProperties.propPriority); - pm->attachProperty(nsName, mStaticProperties.propKeyManagement); - pm->attachProperty(nsName, mStaticProperties.propProtocols); - pm->attachProperty(nsName, mStaticProperties.propAuthAlgorithms); - pm->attachProperty(nsName, mStaticProperties.propPairwiseCiphers); - pm->attachProperty(nsName, mStaticProperties.propGroupCiphers); - pm->attachProperty(nsName, mStaticProperties.propHiddenSsid); - pm->attachProperty(nsName, mStaticProperties.propEnabled); - return 0; -} - -int WifiNetwork::detachProperties(PropertyManager *pm, const char *nsName) { - pm->detachProperty(nsName, mStaticProperties.propEnabled); - pm->detachProperty(nsName, mStaticProperties.propSsid); - pm->detachProperty(nsName, mStaticProperties.propBssid); - pm->detachProperty(nsName, mStaticProperties.propPsk); - pm->detachProperty(nsName, mStaticProperties.propWepKey); - pm->detachProperty(nsName, mStaticProperties.propDefKeyIdx); - pm->detachProperty(nsName, mStaticProperties.propPriority); - pm->detachProperty(nsName, mStaticProperties.propKeyManagement); - pm->detachProperty(nsName, mStaticProperties.propProtocols); - pm->detachProperty(nsName, mStaticProperties.propAuthAlgorithms); - pm->detachProperty(nsName, mStaticProperties.propPairwiseCiphers); - pm->detachProperty(nsName, mStaticProperties.propGroupCiphers); - pm->detachProperty(nsName, mStaticProperties.propHiddenSsid); - return 0; -} - -int WifiNetwork::parseKeyManagementMask(const char *buffer, uint32_t *mask) { - bool none = false; - char *v_tmp = strdup(buffer); - char *v_next = v_tmp; - char *v_token; - -// ALOGD("parseKeyManagementMask(%s)", buffer); - *mask = 0; - - while((v_token = strsep(&v_next, " "))) { - if (!strcasecmp(v_token, "NONE")) { - *mask = KeyManagementMask::NONE; - none = true; - } else if (!none) { - if (!strcasecmp(v_token, "WPA-PSK")) - *mask |= KeyManagementMask::WPA_PSK; - else if (!strcasecmp(v_token, "WPA-EAP")) - *mask |= KeyManagementMask::WPA_EAP; - else if (!strcasecmp(v_token, "IEEE8021X")) - *mask |= KeyManagementMask::IEEE8021X; - else { - ALOGW("Invalid KeyManagementMask value '%s'", v_token); - errno = EINVAL; - free(v_tmp); - return -1; - } - } else { - ALOGW("KeyManagementMask value '%s' when NONE", v_token); - errno = EINVAL; - free(v_tmp); - return -1; - } - } - free(v_tmp); - return 0; -} - -int WifiNetwork::parseProtocolsMask(const char *buffer, uint32_t *mask) { - bool none = false; - char *v_tmp = strdup(buffer); - char *v_next = v_tmp; - char *v_token; - -// ALOGD("parseProtocolsMask(%s)", buffer); - *mask = 0; - while((v_token = strsep(&v_next, " "))) { - if (!strcasecmp(v_token, "WPA")) - *mask |= SecurityProtocolMask::WPA; - else if (!strcasecmp(v_token, "RSN")) - *mask |= SecurityProtocolMask::RSN; - else { - ALOGW("Invalid ProtocolsMask value '%s'", v_token); - errno = EINVAL; - free(v_tmp); - return -1; - } - } - - free(v_tmp); - return 0; -} - -int WifiNetwork::parseAuthAlgorithmsMask(const char *buffer, uint32_t *mask) { - bool none = false; - char *v_tmp = strdup(buffer); - char *v_next = v_tmp; - char *v_token; - -// ALOGD("parseAuthAlgorithmsMask(%s)", buffer); - - *mask = 0; - if (buffer[0] == '\0') - return 0; - - while((v_token = strsep(&v_next, " "))) { - if (!strcasecmp(v_token, "OPEN")) - *mask |= AuthenticationAlgorithmMask::OPEN; - else if (!strcasecmp(v_token, "SHARED")) - *mask |= AuthenticationAlgorithmMask::SHARED; - else if (!strcasecmp(v_token, "LEAP")) - *mask |= AuthenticationAlgorithmMask::LEAP; - else { - ALOGW("Invalid AuthAlgorithmsMask value '%s'", v_token); - errno = EINVAL; - free(v_tmp); - return -1; - } - } - free(v_tmp); - return 0; -} - -int WifiNetwork::parsePairwiseCiphersMask(const char *buffer, uint32_t *mask) { - bool none = false; - char *v_tmp = strdup(buffer); - char *v_next = v_tmp; - char *v_token; - -// ALOGD("parsePairwiseCiphersMask(%s)", buffer); - - *mask = 0; - while((v_token = strsep(&v_next, " "))) { - if (!strcasecmp(v_token, "NONE")) { - *mask = PairwiseCiphersMask::NONE; - none = true; - } else if (!none) { - if (!strcasecmp(v_token, "TKIP")) - *mask |= PairwiseCiphersMask::TKIP; - else if (!strcasecmp(v_token, "CCMP")) - *mask |= PairwiseCiphersMask::CCMP; - else { - ALOGW("PairwiseCiphersMask value '%s' when NONE", v_token); - errno = EINVAL; - free(v_tmp); - return -1; - } - } else { - ALOGW("Invalid PairwiseCiphersMask value '%s'", v_token); - errno = EINVAL; - free(v_tmp); - return -1; - } - } - free(v_tmp); - return 0; -} - -int WifiNetwork::parseGroupCiphersMask(const char *buffer, uint32_t *mask) { - bool none = false; - char *v_tmp = strdup(buffer); - char *v_next = v_tmp; - char *v_token; - -// ALOGD("parseGroupCiphersMask(%s)", buffer); - - *mask = 0; - while((v_token = strsep(&v_next, " "))) { - if (!strcasecmp(v_token, "WEP40")) - *mask |= GroupCiphersMask::WEP40; - else if (!strcasecmp(v_token, "WEP104")) - *mask |= GroupCiphersMask::WEP104; - else if (!strcasecmp(v_token, "TKIP")) - *mask |= GroupCiphersMask::TKIP; - else if (!strcasecmp(v_token, "CCMP")) - *mask |= GroupCiphersMask::CCMP; - else { - ALOGW("Invalid GroupCiphersMask value '%s'", v_token); - errno = EINVAL; - free(v_tmp); - return -1; - } - } - free(v_tmp); - return 0; -} - -WifiNetwork::WifiNetworkIntegerProperty::WifiNetworkIntegerProperty(WifiNetwork *wn, - const char *name, - bool ro, - int elements) : - IntegerProperty(name, ro, elements) { - mWn = wn; -} - -WifiNetwork::WifiNetworkStringProperty::WifiNetworkStringProperty(WifiNetwork *wn, - const char *name, - bool ro, int elements) : - StringProperty(name, ro, elements) { - mWn = wn; -} - -WifiNetwork::WifiNetworkEnabledProperty::WifiNetworkEnabledProperty(WifiNetwork *wn) : - WifiNetworkIntegerProperty(wn, "Enabled", false, 1) { -} - -int WifiNetwork::WifiNetworkEnabledProperty::get(int idx, int *buffer) { - *buffer = mWn->mEnabled; - return 0; -} -int WifiNetwork::WifiNetworkEnabledProperty::set(int idx, int value) { - return mWn->setEnabled(value == 1); -} - -WifiNetwork::WifiNetworkSsidProperty::WifiNetworkSsidProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "Ssid", false, 1) { -} - -int WifiNetwork::WifiNetworkSsidProperty::get(int idx, char *buffer, size_t max) { - strncpy(buffer, - mWn->getSsid() ? mWn->getSsid() : "none", - max); - return 0; -} -int WifiNetwork::WifiNetworkSsidProperty::set(int idx, const char *value) { - return mWn->setSsid(value); -} - -WifiNetwork::WifiNetworkBssidProperty::WifiNetworkBssidProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "Bssid", false, 1) { -} -int WifiNetwork::WifiNetworkBssidProperty::get(int idx, char *buffer, size_t max) { - strncpy(buffer, - mWn->getBssid() ? mWn->getBssid() : "none", - max); - return 0; -} -int WifiNetwork::WifiNetworkBssidProperty::set(int idx, const char *value) { - return mWn->setBssid(value); -} - -WifiNetwork::WifiNetworkPskProperty::WifiNetworkPskProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "Psk", false, 1) { -} -int WifiNetwork::WifiNetworkPskProperty::get(int idx, char *buffer, size_t max) { - strncpy(buffer, - mWn->getPsk() ? mWn->getPsk() : "none", - max); - return 0; -} -int WifiNetwork::WifiNetworkPskProperty::set(int idx, const char *value) { - return mWn->setPsk(value); -} - -WifiNetwork::WifiNetworkWepKeyProperty::WifiNetworkWepKeyProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "WepKey", false, 4) { -} - -int WifiNetwork::WifiNetworkWepKeyProperty::get(int idx, char *buffer, size_t max) { - const char *key = mWn->getWepKey(idx); - - strncpy(buffer, (key ? key : "none"), max); - return 0; -} -int WifiNetwork::WifiNetworkWepKeyProperty::set(int idx, const char *value) { - return mWn->setWepKey(idx, value); -} - -WifiNetwork::WifiNetworkDefaultKeyIndexProperty::WifiNetworkDefaultKeyIndexProperty(WifiNetwork *wn) : - WifiNetworkIntegerProperty(wn, "DefaultKeyIndex", false, 1) { -} -int WifiNetwork::WifiNetworkDefaultKeyIndexProperty::get(int idx, int *buffer) { - *buffer = mWn->getDefaultKeyIndex(); - return 0; -} -int WifiNetwork::WifiNetworkDefaultKeyIndexProperty::set(int idx, int value) { - return mWn->setDefaultKeyIndex(value); -} - -WifiNetwork::WifiNetworkPriorityProperty::WifiNetworkPriorityProperty(WifiNetwork *wn) : - WifiNetworkIntegerProperty(wn, "Priority", false, 1) { -} -int WifiNetwork::WifiNetworkPriorityProperty::get(int idx, int *buffer) { - *buffer = mWn->getPriority(); - return 0; -} -int WifiNetwork::WifiNetworkPriorityProperty::set(int idx, int value) { - return mWn->setPriority(value); -} - -WifiNetwork::WifiNetworkKeyManagementProperty::WifiNetworkKeyManagementProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "KeyManagement", false, 1) { -} -int WifiNetwork::WifiNetworkKeyManagementProperty::get(int idx, char *buffer, size_t max) { - - if (mWn->getKeyManagement() == KeyManagementMask::NONE) - strncpy(buffer, "NONE", max); - else { - char tmp[80] = { '\0' }; - - if (mWn->getKeyManagement() & KeyManagementMask::WPA_PSK) - strcat(tmp, "WPA-PSK"); - if (mWn->getKeyManagement() & KeyManagementMask::WPA_EAP) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "WPA-EAP"); - } - if (mWn->getKeyManagement() & KeyManagementMask::IEEE8021X) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "IEEE8021X"); - } - if (tmp[0] == '\0') { - strncpy(buffer, "(internal error)", max); - errno = ENOENT; - return -1; - } - if (tmp[strlen(tmp)] == ' ') - tmp[strlen(tmp)] = '\0'; - - strncpy(buffer, tmp, max); - } - return 0; -} -int WifiNetwork::WifiNetworkKeyManagementProperty::set(int idx, const char *value) { - uint32_t mask; - if (mWn->parseKeyManagementMask(value, &mask)) - return -1; - return mWn->setKeyManagement(mask); -} - -WifiNetwork::WifiNetworkProtocolsProperty::WifiNetworkProtocolsProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "Protocols", false, 1) { -} -int WifiNetwork::WifiNetworkProtocolsProperty::get(int idx, char *buffer, size_t max) { - char tmp[80] = { '\0' }; - - if (mWn->getProtocols() & SecurityProtocolMask::WPA) - strcat(tmp, "WPA"); - if (mWn->getProtocols() & SecurityProtocolMask::RSN) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "RSN"); - } - - if (tmp[0] == '\0') { - strncpy(buffer, "(internal error)", max); - errno = ENOENT; - return NULL; - } - if (tmp[strlen(tmp)] == ' ') - tmp[strlen(tmp)] = '\0'; - - strncpy(buffer, tmp, max); - return 0; -} -int WifiNetwork::WifiNetworkProtocolsProperty::set(int idx, const char *value) { - uint32_t mask; - if (mWn->parseProtocolsMask(value, &mask)) - return -1; - return mWn->setProtocols(mask); -} - -WifiNetwork::WifiNetworkAuthAlgorithmsProperty::WifiNetworkAuthAlgorithmsProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "AuthAlgorithms", false, 1) { -} -int WifiNetwork::WifiNetworkAuthAlgorithmsProperty::get(int idx, char *buffer, size_t max) { - char tmp[80] = { '\0' }; - - if (mWn->getAuthAlgorithms() == 0) { - strncpy(buffer, "NONE", max); - return 0; - } - - if (mWn->getAuthAlgorithms() & AuthenticationAlgorithmMask::OPEN) - strcat(tmp, "OPEN"); - if (mWn->getAuthAlgorithms() & AuthenticationAlgorithmMask::SHARED) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "SHARED"); - } - if (mWn->getAuthAlgorithms() & AuthenticationAlgorithmMask::LEAP) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "LEAP"); - } - - if (tmp[0] == '\0') { - strncpy(buffer, "(internal error)", max); - errno = ENOENT; - return NULL; - } - if (tmp[strlen(tmp)] == ' ') - tmp[strlen(tmp)] = '\0'; - - strncpy(buffer, tmp, max); - return 0; -} -int WifiNetwork::WifiNetworkAuthAlgorithmsProperty::set(int idx, const char *value) { - uint32_t mask; - if (mWn->parseAuthAlgorithmsMask(value, &mask)) - return -1; - return mWn->setAuthAlgorithms(mask); -} - -WifiNetwork::WifiNetworkPairwiseCiphersProperty::WifiNetworkPairwiseCiphersProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "PairwiseCiphers", false, 1) { -} -int WifiNetwork::WifiNetworkPairwiseCiphersProperty::get(int idx, char *buffer, size_t max) { - if (mWn->getPairwiseCiphers() == PairwiseCiphersMask::NONE) - strncpy(buffer, "NONE", max); - else { - char tmp[80] = { '\0' }; - - if (mWn->getPairwiseCiphers() & PairwiseCiphersMask::TKIP) - strcat(tmp, "TKIP"); - if (mWn->getPairwiseCiphers() & PairwiseCiphersMask::CCMP) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "CCMP"); - } - if (tmp[0] == '\0') { - strncpy(buffer, "(internal error)", max); - errno = ENOENT; - return NULL; - } - if (tmp[strlen(tmp)] == ' ') - tmp[strlen(tmp)] = '\0'; - - strncpy(buffer, tmp, max); - } - return 0; -} -int WifiNetwork::WifiNetworkPairwiseCiphersProperty::set(int idx, const char *value) { - uint32_t mask; - if (mWn->parsePairwiseCiphersMask(value, &mask)) - return -1; - return mWn->setPairwiseCiphers(mask); -} - -WifiNetwork::WifiNetworkGroupCiphersProperty::WifiNetworkGroupCiphersProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "GroupCiphers", false, 1) { -} -int WifiNetwork::WifiNetworkGroupCiphersProperty::get(int idx, char *buffer, size_t max) { - char tmp[80] = { '\0' }; - - if (mWn->getGroupCiphers() & GroupCiphersMask::WEP40) - strcat(tmp, "WEP40"); - if (mWn->getGroupCiphers() & GroupCiphersMask::WEP104) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "WEP104"); - } - if (mWn->getGroupCiphers() & GroupCiphersMask::TKIP) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "TKIP"); - } - if (mWn->getGroupCiphers() & GroupCiphersMask::CCMP) { - if (tmp[0] != '\0') - strcat(tmp, " "); - strcat(tmp, "CCMP"); - } - - if (tmp[0] == '\0') { - strncpy(buffer, "(internal error)", max); - errno = ENOENT; - return -1; - } - if (tmp[strlen(tmp)] == ' ') - tmp[strlen(tmp)] = '\0'; - - strncpy(buffer, tmp, max); - return 0; -} -int WifiNetwork::WifiNetworkGroupCiphersProperty::set(int idx, const char *value) { - uint32_t mask; - if (mWn->parseGroupCiphersMask(value, &mask)) - return -1; - return mWn->setGroupCiphers(mask); -} - -WifiNetwork::WifiNetworkHiddenSsidProperty::WifiNetworkHiddenSsidProperty(WifiNetwork *wn) : - WifiNetworkStringProperty(wn, "HiddenSsid", false, 1) { -} -int WifiNetwork::WifiNetworkHiddenSsidProperty::get(int idx, char *buffer, size_t max) { - const char *scan_ssid = mWn->getHiddenSsid(); - - strncpy(buffer, (scan_ssid ? scan_ssid : "none"), max); - return 0; -} -int WifiNetwork::WifiNetworkHiddenSsidProperty::set(int idx, const char *value) { - return mWn->setHiddenSsid(value); -} diff --git a/nexus/WifiNetwork.h b/nexus/WifiNetwork.h deleted file mode 100644 index 15ec647..0000000 --- a/nexus/WifiNetwork.h +++ /dev/null @@ -1,356 +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. - */ - -#ifndef _WIFI_NETWORK_H -#define _WIFI_NETWORK_H - -#include <sys/types.h> - -#include <utils/List.h> - -#include "Property.h" - -class PropertyManager; - -class KeyManagementMask { -public: - static const uint32_t UNKNOWN = 0; - static const uint32_t NONE = 0x01; - static const uint32_t WPA_PSK = 0x02; - static const uint32_t WPA_EAP = 0x04; - static const uint32_t IEEE8021X = 0x08; - static const uint32_t ALL = WPA_PSK | WPA_EAP | IEEE8021X; -}; - -class SecurityProtocolMask { -public: - static const uint32_t WPA = 0x01; - static const uint32_t RSN = 0x02; -}; - -class AuthenticationAlgorithmMask { -public: - static const uint32_t OPEN = 0x01; - static const uint32_t SHARED = 0x02; - static const uint32_t LEAP = 0x04; -}; - -class PairwiseCiphersMask { -public: - static const uint32_t NONE = 0x00; - static const uint32_t TKIP = 0x01; - static const uint32_t CCMP = 0x02; -}; - -class GroupCiphersMask { -public: - static const uint32_t WEP40 = 0x01; - static const uint32_t WEP104 = 0x02; - static const uint32_t TKIP = 0x04; - static const uint32_t CCMP = 0x08; -}; - -class Supplicant; -class Controller; -class WifiController; - -class WifiNetwork { - class WifiNetworkIntegerProperty : public IntegerProperty { - protected: - WifiNetwork *mWn; - public: - WifiNetworkIntegerProperty(WifiNetwork *wn, const char *name, bool ro, - int elements); - virtual ~WifiNetworkIntegerProperty() {} - virtual int set(int idx, int value) = 0; - virtual int get(int idx, int *buffer) = 0; - }; - friend class WifiNetwork::WifiNetworkIntegerProperty; - - class WifiNetworkStringProperty : public StringProperty { - protected: - WifiNetwork *mWn; - public: - WifiNetworkStringProperty(WifiNetwork *wn, const char *name, bool ro, - int elements); - virtual ~WifiNetworkStringProperty() {} - virtual int set(int idx, const char *value) = 0; - virtual int get(int idx, char *buffer, size_t max) = 0; - }; - friend class WifiNetwork::WifiNetworkStringProperty; - - class WifiNetworkEnabledProperty : public WifiNetworkIntegerProperty { - public: - WifiNetworkEnabledProperty(WifiNetwork *wn); - virtual ~WifiNetworkEnabledProperty() {}; - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiNetworkPriorityProperty : public WifiNetworkIntegerProperty { - public: - WifiNetworkPriorityProperty(WifiNetwork *wn); - virtual ~WifiNetworkPriorityProperty() {}; - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiNetworkDefaultKeyIndexProperty : public WifiNetworkIntegerProperty { - public: - WifiNetworkDefaultKeyIndexProperty(WifiNetwork *wn); - virtual ~WifiNetworkDefaultKeyIndexProperty() {}; - int set(int idx, int value); - int get(int idx, int *buffer); - }; - - class WifiNetworkSsidProperty : public WifiNetworkStringProperty { - public: - WifiNetworkSsidProperty(WifiNetwork *wn); - virtual ~WifiNetworkSsidProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkBssidProperty : public WifiNetworkStringProperty { - public: - WifiNetworkBssidProperty(WifiNetwork *wn); - virtual ~WifiNetworkBssidProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkPskProperty : public WifiNetworkStringProperty { - public: - WifiNetworkPskProperty(WifiNetwork *wn); - virtual ~WifiNetworkPskProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkKeyManagementProperty : public WifiNetworkStringProperty { - public: - WifiNetworkKeyManagementProperty(WifiNetwork *wn); - virtual ~WifiNetworkKeyManagementProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkAuthAlgorithmsProperty : public WifiNetworkStringProperty { - public: - WifiNetworkAuthAlgorithmsProperty(WifiNetwork *wn); - virtual ~WifiNetworkAuthAlgorithmsProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkProtocolsProperty : public WifiNetworkStringProperty { - public: - WifiNetworkProtocolsProperty(WifiNetwork *wn); - virtual ~WifiNetworkProtocolsProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkWepKeyProperty : public WifiNetworkStringProperty { - public: - WifiNetworkWepKeyProperty(WifiNetwork *wn); - virtual ~WifiNetworkWepKeyProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkPairwiseCiphersProperty : public WifiNetworkStringProperty { - public: - WifiNetworkPairwiseCiphersProperty(WifiNetwork *wn); - virtual ~WifiNetworkPairwiseCiphersProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkGroupCiphersProperty : public WifiNetworkStringProperty { - public: - WifiNetworkGroupCiphersProperty(WifiNetwork *wn); - virtual ~WifiNetworkGroupCiphersProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - - class WifiNetworkHiddenSsidProperty : public WifiNetworkStringProperty { - public: - WifiNetworkHiddenSsidProperty(WifiNetwork *wn); - virtual ~WifiNetworkHiddenSsidProperty() {}; - int set(int idx, const char *value); - int get(int idx, char *buffer, size_t max); - }; - -private: - Supplicant *mSuppl; - WifiController *mController; - - /* - * Unique network id - normally provided by supplicant - */ - int mNetid; - - /* - * The networks' SSID. Can either be an ASCII string, - * which must be enclosed in double quotation marks - * (ie: "MyNetwork"), or a string of hex digits which - * are not enclosed in quotes (ie: 01ab7893) - */ - char *mSsid; - - /* - * When set, this entry should only be used - * when associating with the AP having the specified - * BSSID. The value is a string in the format of an - * Ethernet MAC address - */ - char *mBssid; - - /* - * Pre-shared key for use with WPA-PSK - */ - char *mPsk; - - /* - * Up to four WEP keys. Either in ASCII string enclosed in - * double quotes, or a string of hex digits - */ - char *mWepKeys[4]; - - /* - * Default WEP key index, ranging from 0 -> NUM_WEP_KEYS -1 - */ - int mDefaultKeyIndex; - - /* - * Priority determines the preference given to a network by - * supplicant when choosing an access point with which - * to associate - */ - int mPriority; - - /* - * This is a network that does not broadcast it's SSID, so an - * SSID-specific probe request must be used for scans. - */ - char *mHiddenSsid; - - /* - * The set of key management protocols supported by this configuration. - */ - uint32_t mKeyManagement; - - /* - * The set of security protocols supported by this configuration. - */ - uint32_t mProtocols; - - /* - * The set of authentication protocols supported by this configuration. - */ - uint32_t mAuthAlgorithms; - - /* - * The set of pairwise ciphers for WPA supported by this configuration. - */ - uint32_t mPairwiseCiphers; - - /* - * The set of group ciphers for WPA supported by this configuration. - */ - uint32_t mGroupCiphers; - - /* - * Set if this Network is enabled - */ - bool mEnabled; - - char *mPropNamespace; - struct { - WifiNetworkEnabledProperty *propEnabled; - WifiNetworkSsidProperty *propSsid; - WifiNetworkBssidProperty *propBssid; - WifiNetworkPskProperty *propPsk; - WifiNetworkWepKeyProperty *propWepKey; - WifiNetworkDefaultKeyIndexProperty *propDefKeyIdx; - WifiNetworkPriorityProperty *propPriority; - WifiNetworkKeyManagementProperty *propKeyManagement; - WifiNetworkProtocolsProperty *propProtocols; - WifiNetworkAuthAlgorithmsProperty *propAuthAlgorithms; - WifiNetworkPairwiseCiphersProperty *propPairwiseCiphers; - WifiNetworkGroupCiphersProperty *propGroupCiphers; - WifiNetworkHiddenSsidProperty *propHiddenSsid; - } mStaticProperties; -private: - WifiNetwork(); - -public: - WifiNetwork(WifiController *c, Supplicant *suppl, int networkId); - WifiNetwork(WifiController *c, Supplicant *suppl, const char *data); - - virtual ~WifiNetwork(); - - WifiNetwork *clone(); - int attachProperties(PropertyManager *pm, const char *nsName); - int detachProperties(PropertyManager *pm, const char *nsName); - - int getNetworkId() { return mNetid; } - const char *getSsid() { return mSsid; } - const char *getBssid() { return mBssid; } - const char *getPsk() { return mPsk; } - const char *getWepKey(int idx) { return mWepKeys[idx]; } - int getDefaultKeyIndex() { return mDefaultKeyIndex; } - int getPriority() { return mPriority; } - const char *getHiddenSsid() { return mHiddenSsid; } - uint32_t getKeyManagement() { return mKeyManagement; } - uint32_t getProtocols() { return mProtocols; } - uint32_t getAuthAlgorithms() { return mAuthAlgorithms; } - uint32_t getPairwiseCiphers() { return mPairwiseCiphers; } - uint32_t getGroupCiphers() { return mGroupCiphers; } - bool getEnabled() { return mEnabled; } - Controller *getController() { return (Controller *) mController; } - - int setEnabled(bool enabled); - int setSsid(const char *ssid); - int setBssid(const char *bssid); - int setPsk(const char *psk); - int setWepKey(int idx, const char *key); - int setDefaultKeyIndex(int idx); - int setPriority(int pri); - int setHiddenSsid(const char *ssid); - int setKeyManagement(uint32_t mask); - int setProtocols(uint32_t mask); - int setAuthAlgorithms(uint32_t mask); - int setPairwiseCiphers(uint32_t mask); - int setGroupCiphers(uint32_t mask); - - // XXX:Should this really be exposed?.. meh - int refresh(); - -private: - int parseKeyManagementMask(const char *buffer, uint32_t *mask); - int parseProtocolsMask(const char *buffer, uint32_t *mask); - int parseAuthAlgorithmsMask(const char *buffer, uint32_t *mask); - int parsePairwiseCiphersMask(const char *buffer, uint32_t *mask); - int parseGroupCiphersMask(const char *buffer, uint32_t *mask); - void createProperties(); -}; - -typedef android::List<WifiNetwork *> WifiNetworkCollection; - -#endif diff --git a/nexus/WifiScanner.cpp b/nexus/WifiScanner.cpp deleted file mode 100644 index 4c956ac..0000000 --- a/nexus/WifiScanner.cpp +++ /dev/null @@ -1,103 +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. - */ - -#include <stdlib.h> -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/time.h> -#include <sys/types.h> -#include <errno.h> -#include <pthread.h> - -#define LOG_TAG "WifiScanner" -#include <cutils/log.h> - -#include "WifiScanner.h" -#include "Supplicant.h" - -extern "C" int pthread_cancel(pthread_t thread); - -WifiScanner::WifiScanner(Supplicant *suppl, int period) { - mSuppl = suppl; - mPeriod = period; - mActive = false; -} - -int WifiScanner::start(bool active) { - mActive = active; - - if(pipe(mCtrlPipe)) - return -1; - - if (pthread_create(&mThread, NULL, WifiScanner::threadStart, this)) - return -1; - return 0; -} - -void *WifiScanner::threadStart(void *obj) { - WifiScanner *me = reinterpret_cast<WifiScanner *>(obj); - me->run(); - pthread_exit(NULL); - return NULL; -} - -int WifiScanner::stop() { - char c = 0; - - if (write(mCtrlPipe[1], &c, 1) != 1) { - ALOGE("Error writing to control pipe (%s)", strerror(errno)); - return -1; - } - - void *ret; - if (pthread_join(mThread, &ret)) { - ALOGE("Error joining to scanner thread (%s)", strerror(errno)); - return -1; - } - - close(mCtrlPipe[0]); - close(mCtrlPipe[1]); - return 0; -} - -void WifiScanner::run() { - ALOGD("Starting wifi scanner (active = %d)", mActive); - - while(1) { - fd_set read_fds; - struct timeval to; - int rc = 0; - - to.tv_usec = 0; - to.tv_sec = mPeriod; - - FD_ZERO(&read_fds); - FD_SET(mCtrlPipe[0], &read_fds); - - if (mSuppl->triggerScan(mActive)) { - ALOGW("Error triggering scan (%s)", strerror(errno)); - } - - if ((rc = select(mCtrlPipe[0] + 1, &read_fds, NULL, NULL, &to)) < 0) { - ALOGE("select failed (%s) - sleeping for one scanner period", strerror(errno)); - sleep(mPeriod); - continue; - } else if (!rc) { - } else if (FD_ISSET(mCtrlPipe[0], &read_fds)) - break; - } // while - ALOGD("Stopping wifi scanner"); -} diff --git a/nexus/WifiScanner.h b/nexus/WifiScanner.h deleted file mode 100644 index 92822e9..0000000 --- a/nexus/WifiScanner.h +++ /dev/null @@ -1,47 +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. - */ - -#ifndef _WIFISCANNER_H -#define _WIFISCANNER_H - -#include <pthread.h> - -class Supplicant; - -class WifiScanner { - pthread_t mThread; - int mCtrlPipe[2]; - Supplicant *mSuppl; - int mPeriod; - bool mActive; - - -public: - WifiScanner(Supplicant *suppl, int period); - virtual ~WifiScanner() {} - - int getPeriod() { return mPeriod; } - - int start(bool active); - int stop(); - -private: - static void *threadStart(void *obj); - - void run(); -}; - -#endif diff --git a/nexus/WifiStatusPoller.cpp b/nexus/WifiStatusPoller.cpp deleted file mode 100644 index 015af5d..0000000 --- a/nexus/WifiStatusPoller.cpp +++ /dev/null @@ -1,104 +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. - */ -#include <stdio.h> -#include <errno.h> -#include <stdlib.h> -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/un.h> - -#define LOG_TAG "WifiStatusPoller" -#include <cutils/log.h> - -#include "WifiStatusPoller.h" -#include "IWifiStatusPollerHandler.h" - - -WifiStatusPoller::WifiStatusPoller(IWifiStatusPollerHandler *handler) : - mHandlers(handler) { - mPollingInterval = 5; - mStarted = false; -} - -int WifiStatusPoller::start() { - - if (pipe(mCtrlPipe)) - return -1; - - if (pthread_create(&mThread, NULL, WifiStatusPoller::threadStart, this)) - return -1; - - return 0; -} - -int WifiStatusPoller::stop() { - char c = 0; - - if (write(mCtrlPipe[1], &c, 1) != 1) { - ALOGE("Error writing to control pipe (%s)", strerror(errno)); - return -1; - } - - void *ret; - if (pthread_join(mThread, &ret)) { - ALOGE("Error joining to listener thread (%s)", strerror(errno)); - return -1; - } - close(mCtrlPipe[0]); - close(mCtrlPipe[1]); - return 0; -} - -void *WifiStatusPoller::threadStart(void *obj) { - WifiStatusPoller *me = reinterpret_cast<WifiStatusPoller *>(obj); - - me->mStarted = true; - ALOGD("Starting"); - me->run(); - me->mStarted = false; - ALOGD("Stopping"); - pthread_exit(NULL); - return NULL; -} - -void WifiStatusPoller::run() { - - while(1) { - struct timeval to; - fd_set read_fds; - int rc = 0; - int max = 0; - - FD_ZERO(&read_fds); - to.tv_usec = 0; - to.tv_sec = mPollingInterval; - - FD_SET(mCtrlPipe[0], &read_fds); - max = mCtrlPipe[0]; - - if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) { - ALOGE("select failed (%s)", strerror(errno)); - sleep(1); - continue; - } else if (!rc) { - mHandlers->onStatusPollInterval(); - } - if (FD_ISSET(mCtrlPipe[0], &read_fds)) - break; - } -} diff --git a/nexus/WifiStatusPoller.h b/nexus/WifiStatusPoller.h deleted file mode 100644 index 202bbbf..0000000 --- a/nexus/WifiStatusPoller.h +++ /dev/null @@ -1,47 +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. - */ - -#ifndef _WIFI_STATUS_POLLER_H -#define _WIFI_STATUS_POLLER_H - -#include <pthread.h> - -class IWifiStatusPollerHandler; - -class WifiStatusPoller { - pthread_t mThread; - int mCtrlPipe[2]; - int mPollingInterval; - IWifiStatusPollerHandler *mHandlers; - bool mStarted; - -public: - WifiStatusPoller(IWifiStatusPollerHandler *handler); - virtual ~WifiStatusPoller() {} - - int start(); - int stop(); - bool isStarted() { return mStarted; } - - void setPollingInterval(int interval); - int getPollingInterval() { return mPollingInterval; } - -private: - static void *threadStart(void *obj); - void run(); -}; - -#endif diff --git a/nexus/main.cpp b/nexus/main.cpp deleted file mode 100644 index 7d2af02..0000000 --- a/nexus/main.cpp +++ /dev/null @@ -1,67 +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. - */ - -#include <stdlib.h> -#include <errno.h> - -#define LOG_TAG "Nexus" - -#include "cutils/log.h" -#include "NetworkManager.h" -#include "CommandListener.h" - -#include "LoopController.h" -#include "OpenVpnController.h" -#include "TiwlanWifiController.h" - -int main() { - ALOGI("Nexus version 0.1 firing up"); - - CommandListener *cl = new CommandListener(); - - NetworkManager *nm; - if (!(nm = NetworkManager::Instance())) { - ALOGE("Unable to create NetworkManager"); - exit (-1); - }; - - nm->setBroadcaster((SocketListener *) cl); - - nm->attachController(new LoopController(nm->getPropMngr(), nm)); - nm->attachController(new TiwlanWifiController(nm->getPropMngr(), nm, "/system/lib/modules/wlan.ko", "wlan", "")); -// nm->attachController(new AndroidL2TPVpnController(nm->getPropMngr(), nm)); - nm->attachController(new OpenVpnController(nm->getPropMngr(), nm)); - - - if (NetworkManager::Instance()->run()) { - ALOGE("Unable to Run NetworkManager (%s)", strerror(errno)); - exit (1); - } - - if (cl->startListener()) { - ALOGE("Unable to start CommandListener (%s)", strerror(errno)); - exit (1); - } - - // XXX: we'll use the main thread for the NetworkManager eventually - - while(1) { - sleep(1000); - } - - ALOGI("Nexus exiting"); - exit(0); -} diff --git a/nexus/nexctl.c b/nexus/nexctl.c deleted file mode 100644 index 8e1d90c..0000000 --- a/nexus/nexctl.c +++ /dev/null @@ -1,148 +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. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <signal.h> -#include <errno.h> -#include <fcntl.h> - -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cutils/sockets.h> -#include <private/android_filesystem_config.h> - -static void usage(char *progname); -static int do_monitor(int sock, int stop_after_cmd); -static int do_cmd(int sock, int argc, char **argv); - -int main(int argc, char **argv) { - int sock; - - if (argc < 2) - usage(argv[0]); - - if ((sock = socket_local_client("nexus", - ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM)) < 0) { - fprintf(stderr, "Error connecting (%s)\n", strerror(errno)); - exit(4); - } - - if (!strcmp(argv[1], "monitor")) - exit(do_monitor(sock, 0)); - exit(do_cmd(sock, argc, argv)); -} - -static int do_cmd(int sock, int argc, char **argv) { - char final_cmd[255] = { '\0' }; - int i; - - for (i = 1; i < argc; i++) { - char *cmp; - - if (!index(argv[i], ' ')) - asprintf(&cmp, "%s%s", argv[i], (i == (argc -1)) ? "" : " "); - else - asprintf(&cmp, "\"%s\"%s", argv[i], (i == (argc -1)) ? "" : " "); - - strcat(final_cmd, cmp); - free(cmp); - } - - if (write(sock, final_cmd, strlen(final_cmd) + 1) < 0) { - perror("write"); - return errno; - } - - return do_monitor(sock, 1); -} - -static int do_monitor(int sock, int stop_after_cmd) { - char *buffer = malloc(4096); - - if (!stop_after_cmd) - printf("[Connected to Nexus]\n"); - - while(1) { - fd_set read_fds; - struct timeval to; - int rc = 0; - - to.tv_sec = 10; - to.tv_usec = 0; - - FD_ZERO(&read_fds); - FD_SET(sock, &read_fds); - - if ((rc = select(sock +1, &read_fds, NULL, NULL, &to)) < 0) { - fprintf(stderr, "Error in select (%s)\n", strerror(errno)); - free(buffer); - return errno; - } else if (!rc) { - continue; - fprintf(stderr, "[TIMEOUT]\n"); - return ETIMEDOUT; - } else if (FD_ISSET(sock, &read_fds)) { - memset(buffer, 0, 4096); - if ((rc = read(sock, buffer, 4096)) <= 0) { - if (rc == 0) - fprintf(stderr, "Lost connection to Nexus - did it crash?\n"); - else - fprintf(stderr, "Error reading data (%s)\n", strerror(errno)); - free(buffer); - if (rc == 0) - return ECONNRESET; - return errno; - } - - int offset = 0; - int i = 0; - - for (i = 0; i < rc; i++) { - if (buffer[i] == '\0') { - int code; - char tmp[4]; - - strncpy(tmp, buffer + offset, 3); - tmp[3] = '\0'; - code = atoi(tmp); - - printf("%s\n", buffer + offset); - if (stop_after_cmd) { - if (code >= 200 && code < 600) - return 0; - } - offset = i + 1; - } - } - } - } - free(buffer); - return 0; -} - -static void usage(char *progname) { - fprintf(stderr, "Usage: %s <monitor>|<cmd> [arg1] [arg2...]\n", progname); - exit(1); -} - diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 1a9e06f..e62c3ea 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -50,6 +50,8 @@ $(file) : $(LOCAL_PATH)/ueventd.rc | $(ACP) ALL_PREBUILT += $(file) $(INSTALLED_RAMDISK_TARGET): $(file) +# init.usb.rc is handled by build/target/product/core.rc + # 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. diff --git a/rootdir/etc/init.goldfish.rc b/rootdir/etc/init.goldfish.rc index 83b7f8a..cde9dee 100644 --- a/rootdir/etc/init.goldfish.rc +++ b/rootdir/etc/init.goldfish.rc @@ -5,6 +5,10 @@ on early-init symlink /mnt/sdcard /sdcard on boot + setsebool in_qemu=1 + restorecon /sys/qemu_trace/process_name + restorecon /sys/qemu_trace/state + restorecon /sys/qemu_trace/symbol setprop ARGH ARGH setprop net.eth0.gw 10.0.2.2 setprop net.eth0.dns1 10.0.2.3 diff --git a/rootdir/init.rc b/rootdir/init.rc index 236c97b..054c2ca 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -1,9 +1,21 @@ +# Copyright (C) 2012 The Android Open Source Project +# +# IMPORTANT: Do not create world writable files or directories. +# This is a common source of Android security bugs. +# + import /init.${ro.hardware}.rc +import /init.usb.rc +import /init.trace.rc on early-init # Set init and its forked children's oom_adj. write /proc/1/oom_adj -16 + # Set the security context for the init process. + # This should occur before anything else (e.g. ueventd) is started. + setcon u:r:init:s0 + start ueventd # create mountpoints @@ -24,7 +36,7 @@ loglevel 3 export ANDROID_DATA /data export ASEC_MOUNTPOINT /mnt/asec export LOOP_MOUNTPOINT /mnt/obb - export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar + export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar # Backward compatibility symlink /system/etc /etc @@ -70,25 +82,36 @@ loglevel 3 write /proc/sys/kernel/sched_compat_yield 1 write /proc/sys/kernel/sched_child_runs_first 0 write /proc/sys/kernel/randomize_va_space 2 + write /proc/sys/kernel/kptr_restrict 2 + write /proc/sys/kernel/dmesg_restrict 1 + write /proc/sys/vm/mmap_min_addr 32768 + write /proc/sys/kernel/sched_rt_runtime_us 950000 + write /proc/sys/kernel/sched_rt_period_us 1000000 # Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu chown system system /dev/cpuctl chown system system /dev/cpuctl/tasks - chmod 0777 /dev/cpuctl/tasks + chmod 0660 /dev/cpuctl/tasks write /dev/cpuctl/cpu.shares 1024 - - mkdir /dev/cpuctl/fg_boost - chown system system /dev/cpuctl/fg_boost/tasks - chmod 0777 /dev/cpuctl/fg_boost/tasks - write /dev/cpuctl/fg_boost/cpu.shares 1024 - - mkdir /dev/cpuctl/bg_non_interactive - chown system system /dev/cpuctl/bg_non_interactive/tasks - chmod 0777 /dev/cpuctl/bg_non_interactive/tasks + write /dev/cpuctl/cpu.rt_runtime_us 950000 + write /dev/cpuctl/cpu.rt_period_us 1000000 + + mkdir /dev/cpuctl/apps + chown system system /dev/cpuctl/apps/tasks + chmod 0666 /dev/cpuctl/apps/tasks + write /dev/cpuctl/apps/cpu.shares 1024 + write /dev/cpuctl/apps/cpu.rt_runtime_us 800000 + write /dev/cpuctl/apps/cpu.rt_period_us 1000000 + + mkdir /dev/cpuctl/apps/bg_non_interactive + chown system system /dev/cpuctl/apps/bg_non_interactive/tasks + chmod 0666 /dev/cpuctl/apps/bg_non_interactive/tasks # 5.0 % - write /dev/cpuctl/bg_non_interactive/cpu.shares 52 + write /dev/cpuctl/apps/bg_non_interactive/cpu.shares 52 + write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_runtime_us 700000 + write /dev/cpuctl/apps/bg_non_interactive/cpu.rt_period_us 1000000 # Allow everybody to read the xt_qtaguid resource tracking misc dev. # This is needed by any process that uses socket tagging. @@ -109,10 +132,14 @@ on post-fs # We chown/chmod /cache again so because mount is run as root + defaults chown system cache /cache chmod 0770 /cache + # We restorecon /cache in case the cache partition has been reset. + restorecon /cache # This may have been created by the recovery system with odd permissions chown system cache /cache/recovery chmod 0770 /cache/recovery + # This may have been created by the recovery system with the wrong context. + restorecon /cache/recovery #change permissions on vmallocinfo so we can grab it from bugreports chown root log /proc/vmallocinfo @@ -131,6 +158,8 @@ on post-fs-data # We chown/chmod /data again so because mount is run as root + defaults chown system system /data chmod 0771 /data + # We restorecon /data in case the userdata partition has been reset. + restorecon /data # Create dump dir and collect dumps. # Do this before we mount cache so eventually we can use cache for @@ -160,11 +189,17 @@ on post-fs-data mkdir /data/misc/wifi 0770 wifi wifi chmod 0660 /data/misc/wifi/wpa_supplicant.conf mkdir /data/local 0751 root root + + # For security reasons, /data/local/tmp should always be empty. + # Do not place files or directories in /data/local/tmp mkdir /data/local/tmp 0771 shell shell mkdir /data/data 0771 system system mkdir /data/app-private 0771 system system + mkdir /data/app-asec 0700 root root mkdir /data/app 0771 system system mkdir /data/property 0700 root root + mkdir /data/ssh 0750 root shell + mkdir /data/ssh/empty 0700 root root # create dalvik-cache, so as to enforce our permissions mkdir /data/dalvik-cache 0771 system system @@ -187,11 +222,6 @@ on post-fs-data # Set indication (checked by vold) that we have finished this action #setprop vold.post_fs_data_done 1 - chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file - chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file - chown system system /sys/class/android_usb/android0/f_rndis/ethaddr - chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr - on boot # basic network init ifup lo @@ -203,7 +233,7 @@ on boot # Memory management. Basic kernel parameters, and allow the high # level system server to be able to adjust the kernel OOM driver -# paramters to match how it is managing things. +# parameters to match how it is managing things. write /proc/sys/vm/overcommit_memory 1 write /proc/sys/vm/min_free_order_shift 4 chown root system /sys/module/lowmemorykiller/parameters/adj @@ -221,12 +251,34 @@ on boot chown radio system /sys/android_power/acquire_full_wake_lock chown radio system /sys/android_power/acquire_partial_wake_lock chown radio system /sys/android_power/release_wake_lock - chown radio system /sys/power/state + chown system system /sys/power/state + chown system system /sys/power/wakeup_count chown radio system /sys/power/wake_lock chown radio system /sys/power/wake_unlock chmod 0660 /sys/power/state chmod 0660 /sys/power/wake_lock chmod 0660 /sys/power/wake_unlock + + chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate + chown system system /sys/devices/system/cpu/cpufreq/interactive/min_sample_time + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/min_sample_time + chown system system /sys/devices/system/cpu/cpufreq/interactive/hispeed_freq + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/hispeed_freq + chown system system /sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load + chown system system /sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay + chown system system /sys/devices/system/cpu/cpufreq/interactive/boost + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/boost + chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse + chown system system /sys/devices/system/cpu/cpufreq/interactive/input_boost + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/input_boost + + # Assume SMP uses shared cpufreq policy for all CPUs + chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq + chmod 0660 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq + chown system system /sys/class/timed_output/vibrator/enable chown system system /sys/class/leds/keyboard-backlight/brightness chown system system /sys/class/leds/lcd-backlight/brightness @@ -296,54 +348,12 @@ on property:vold.decrypt=trigger_shutdown_framework class_reset late_start class_reset main -# Used to disable USB when switching states -on property:sys.usb.config=none - stop adbd - write /sys/class/android_usb/android0/enable 0 - write /sys/class/android_usb/android0/bDeviceClass 0 - setprop sys.usb.state ${sys.usb.config} - -# adb only USB configuration -# This should only be used during device bringup -# and as a fallback if the USB manager fails to set a standard configuration -on property:sys.usb.config=adb - write /sys/class/android_usb/android0/enable 0 - write /sys/class/android_usb/android0/idVendor 18d1 - write /sys/class/android_usb/android0/idProduct D002 - write /sys/class/android_usb/android0/functions ${sys.usb.config} - write /sys/class/android_usb/android0/enable 1 - start adbd - setprop sys.usb.state ${sys.usb.config} - -# USB accessory configuration -on property:sys.usb.config=accessory - write /sys/class/android_usb/android0/enable 0 - write /sys/class/android_usb/android0/idVendor 18d1 - write /sys/class/android_usb/android0/idProduct 2d00 - write /sys/class/android_usb/android0/functions ${sys.usb.config} - write /sys/class/android_usb/android0/enable 1 - setprop sys.usb.state ${sys.usb.config} - -# USB accessory configuration, with adb -on property:sys.usb.config=accessory,adb - write /sys/class/android_usb/android0/enable 0 - write /sys/class/android_usb/android0/idVendor 18d1 - write /sys/class/android_usb/android0/idProduct 2d01 - write /sys/class/android_usb/android0/functions ${sys.usb.config} - write /sys/class/android_usb/android0/enable 1 - start adbd - setprop sys.usb.state ${sys.usb.config} - -# Used to set USB configuration at boot and to switch the configuration -# when changing the default configuration -on property:persist.sys.usb.config=* - setprop sys.usb.config ${persist.sys.usb.config} - ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd class core critical + seclabel u:r:ueventd:s0 service console /system/bin/sh class core @@ -359,23 +369,12 @@ on property:ro.debuggable=1 service adbd /sbin/adbd class core disabled + seclabel u:r:adbd:s0 # adbd on at boot in emulator on property:ro.kernel.qemu=1 start adbd -# This property trigger has added to imitiate the previous behavior of "adb root". -# The adb gadget driver used to reset the USB bus when the adbd daemon exited, -# and the host side adb relied on this behavior to force it to reconnect with the -# new adbd instance after init relaunches it. So now we force the USB bus to reset -# here when adbd sets the service.adb.root property to 1. We also restart adbd here -# rather than waiting for init to notice its death and restarting it so the timing -# of USB resetting and adb restarting more closely matches the previous behavior. -on property:service.adb.root=1 - write /sys/class/android_usb/android0/enable 0 - restart adbd - write /sys/class/android_usb/android0/enable 1 - service servicemanager /system/bin/servicemanager class core user system @@ -395,6 +394,7 @@ service netd /system/bin/netd class main socket netd stream 0660 root system socket dnsproxyd stream 0660 root inet + socket mdns stream 0660 root system service debuggerd /system/bin/debuggerd class main @@ -423,7 +423,7 @@ service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-sys service drm /system/bin/drmserver class main user drm - group system inet drmrpc + group drm system inet drmrpc sdcard_r service media /system/bin/mediaserver class main @@ -480,7 +480,7 @@ service mtpd /system/bin/mtpd service keystore /system/bin/keystore /data/misc/keystore class main user keystore - group keystore + group keystore drmrpc socket keystore stream 666 service dumpstate /system/bin/dumpstate -s @@ -488,3 +488,15 @@ service dumpstate /system/bin/dumpstate -s socket dumpstate stream 0660 shell log disabled oneshot + +service sshd /system/bin/start-ssh + class main + disabled + +service mdnsd /system/bin/mdnsd + class main + user mdnsr + group inet net_raw + socket mdnsd stream 0660 mdnsr inet + disabled + oneshot diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc new file mode 100644 index 0000000..1d114f5 --- /dev/null +++ b/rootdir/init.trace.rc @@ -0,0 +1,31 @@ +## Permissions to allow system-wide tracing to the kernel trace buffer. +## +on boot + +# Allow writing to the kernel trace log. + chmod 0222 /sys/kernel/debug/tracing/trace_marker + +# Allow the shell group to enable (some) kernel tracing. + chown root shell /sys/kernel/debug/tracing/trace_clock + chown root shell /sys/kernel/debug/tracing/buffer_size_kb + chown root shell /sys/kernel/debug/tracing/options/overwrite + chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable + chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable + chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable + chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable + chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable + chown root shell /sys/kernel/debug/tracing/tracing_on + + chmod 0664 /sys/kernel/debug/tracing/trace_clock + chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb + chmod 0664 /sys/kernel/debug/tracing/options/overwrite + chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable + chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable + chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable + chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable + chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable + chmod 0664 /sys/kernel/debug/tracing/tracing_on + +# Allow only the shell group to read and truncate the kernel trace. + chown root shell /sys/kernel/debug/tracing/trace + chmod 0660 /sys/kernel/debug/tracing/trace diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc new file mode 100644 index 0000000..15467cc --- /dev/null +++ b/rootdir/init.usb.rc @@ -0,0 +1,91 @@ +# Copyright (C) 2012 The Android Open Source Project +# +# USB configuration common for all android devices +# + +on post-fs-data + chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file + chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file + chown system system /sys/class/android_usb/android0/f_rndis/ethaddr + chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr + +# Used to disable USB when switching states +on property:sys.usb.config=none + stop adbd + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/bDeviceClass 0 + setprop sys.usb.state ${sys.usb.config} + +# adb only USB configuration +# This should only be used during device bringup +# and as a fallback if the USB manager fails to set a standard configuration +on property:sys.usb.config=adb + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct D002 + write /sys/class/android_usb/android0/functions ${sys.usb.config} + write /sys/class/android_usb/android0/enable 1 + start adbd + setprop sys.usb.state ${sys.usb.config} + +# USB accessory configuration +on property:sys.usb.config=accessory + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d00 + write /sys/class/android_usb/android0/functions ${sys.usb.config} + write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +# USB accessory configuration, with adb +on property:sys.usb.config=accessory,adb + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d01 + write /sys/class/android_usb/android0/functions ${sys.usb.config} + write /sys/class/android_usb/android0/enable 1 + start adbd + setprop sys.usb.state ${sys.usb.config} + +# audio accessory configuration +on property:sys.usb.config=audio_source + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d02 + write /sys/class/android_usb/android0/functions ${sys.usb.config} + write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +# audio accessory configuration, with adb +on property:sys.usb.config=audio_source,adb + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d03 + write /sys/class/android_usb/android0/functions ${sys.usb.config} + write /sys/class/android_usb/android0/enable 1 + start adbd + setprop sys.usb.state ${sys.usb.config} + +# USB and audio accessory configuration +on property:sys.usb.config=accessory,audio_source + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d04 + write /sys/class/android_usb/android0/functions ${sys.usb.config} + write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +# USB and audio accessory configuration, with adb +on property:sys.usb.config=accessory,audio_source,adb + write /sys/class/android_usb/android0/enable 0 + write /sys/class/android_usb/android0/idVendor 18d1 + write /sys/class/android_usb/android0/idProduct 2d05 + write /sys/class/android_usb/android0/functions ${sys.usb.config} + write /sys/class/android_usb/android0/enable 1 + start adbd + setprop sys.usb.state ${sys.usb.config} + +# Used to set USB configuration at boot and to switch the configuration +# when changing the default configuration +on property:persist.sys.usb.config=* + setprop sys.usb.config ${persist.sys.usb.config} diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index 438cf0a..07624c4 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -8,8 +8,9 @@ /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 +# Anyone can read the logs, but if they're not in the "logs" +# group, then they'll only see log entries for their UID. +/dev/log/* 0666 root log # the msm hw3d client device node is world writable/readable. /dev/msm_hw3dc 0666 root root diff --git a/run-as/Android.mk b/run-as/Android.mk index 326f5af..043cc3a 100644 --- a/run-as/Android.mk +++ b/run-as/Android.mk @@ -5,8 +5,4 @@ LOCAL_SRC_FILES:= run-as.c package.c LOCAL_MODULE:= run-as -LOCAL_FORCE_STATIC_EXECUTABLE := true - -LOCAL_STATIC_LIBRARIES := libc - include $(BUILD_EXECUTABLE) diff --git a/run-as/package.c b/run-as/package.c index ca08436..143d647 100644 --- a/run-as/package.c +++ b/run-as/package.c @@ -18,6 +18,7 @@ #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> +#include <sys/mman.h> #include <private/android_filesystem_config.h> #include "package.h" @@ -43,9 +44,6 @@ /* The file containing the list of installed packages on the system */ #define PACKAGES_LIST_FILE "/data/system/packages.list" -/* This should be large enough to hold the content of the package database file */ -#define PACKAGES_LIST_BUFFER_SIZE 65536 - /* Copy 'srclen' string bytes from 'src' into buffer 'dst' of size 'dstlen' * This function always zero-terminate the destination buffer unless * 'dstlen' is 0, even in case of overflow. @@ -67,40 +65,73 @@ string_copy(char* dst, size_t dstlen, const char* src, size_t srclen) *dst = '\0'; /* zero-terminate result */ } -/* Read up to 'buffsize' bytes into 'buff' from the file - * named 'filename'. Return byte length on success, or -1 - * on error. +/* Open 'filename' and map it into our address-space. + * Returns buffer address, or NULL on error + * On exit, *filesize will be set to the file's size, or 0 on error */ -static int -read_file(const char* filename, char* buff, size_t buffsize) +static void* +map_file(const char* filename, size_t* filesize) { - int fd, len, old_errno; + int fd, ret, old_errno; + struct stat st; + size_t length = 0; + void* address = NULL; - /* check the input buffer size */ - if (buffsize >= INT_MAX) { - errno = EINVAL; - return -1; - } + *filesize = 0; /* open the file for reading */ - do { - fd = open(filename, O_RDONLY); - } while (fd < 0 && errno == EINTR); - + fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); if (fd < 0) - return -1; + return NULL; - /* read the content */ - do { - len = read(fd, buff, buffsize); - } while (len < 0 && errno == EINTR); + /* get its size */ + ret = TEMP_FAILURE_RETRY(fstat(fd, &st)); + if (ret < 0) + goto EXIT; + + /* Ensure that the file is owned by the system user */ + if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_SYSTEM)) { + goto EXIT; + } + + /* Ensure that the file has sane permissions */ + if ((st.st_mode & S_IWOTH) != 0) { + goto EXIT; + } + + /* Ensure that the size is not ridiculously large */ + length = (size_t)st.st_size; + if ((off_t)length != st.st_size) { + errno = ENOMEM; + goto EXIT; + } + /* Memory-map the file now */ + address = TEMP_FAILURE_RETRY(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0)); + if (address == MAP_FAILED) { + address = NULL; + goto EXIT; + } + + /* We're good, return size */ + *filesize = length; + +EXIT: /* close the file, preserve old errno for better diagnostics */ old_errno = errno; close(fd); errno = old_errno; - return len; + return address; +} + +/* unmap the file, but preserve errno */ +static void +unmap_file(void* address, size_t size) +{ + int old_errno = errno; + TEMP_FAILURE_RETRY(munmap(address, size)); + errno = old_errno; } /* Check that a given directory: @@ -371,18 +402,18 @@ BAD: int get_package_info(const char* pkgName, PackageInfo *info) { - static char buffer[PACKAGES_LIST_BUFFER_SIZE]; - int buffer_len; + char* buffer; + size_t buffer_len; const char* p; const char* buffer_end; - int result; + int result = -1; info->uid = 0; info->isDebuggable = 0; info->dataDir[0] = '\0'; - buffer_len = read_file(PACKAGES_LIST_FILE, buffer, sizeof buffer); - if (buffer_len < 0) + buffer = map_file(PACKAGES_LIST_FILE, &buffer_len); + if (buffer == NULL) return -1; p = buffer; @@ -455,7 +486,8 @@ get_package_info(const char* pkgName, PackageInfo *info) string_copy(info->dataDir, sizeof info->dataDir, p, q - p); /* Ignore the rest */ - return 0; + result = 0; + goto EXIT; NEXT_LINE: p = next; @@ -463,9 +495,14 @@ get_package_info(const char* pkgName, PackageInfo *info) /* the package is unknown */ errno = ENOENT; - return -1; + result = -1; + goto EXIT; BAD_FORMAT: errno = EINVAL; - return -1; + result = -1; + +EXIT: + unmap_file(buffer, buffer_len); + return result; } diff --git a/run-as/run-as.c b/run-as/run-as.c index d2a44e1..20e1530 100644 --- a/run-as/run-as.c +++ b/run-as/run-as.c @@ -41,9 +41,9 @@ * * - This program should only run for the 'root' or 'shell' users * - * - Statically link against the C library, and avoid anything that - * is more complex than simple system calls until the uid/gid has - * been dropped to that of a normal user or you are sure to exit. + * - Avoid anything that is more complex than simple system calls + * until the uid/gid has been dropped to that of a normal user + * or you are sure to exit. * * This avoids depending on environment variables, system properties * and other external factors that may affect the C library in diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 689cd2a..a95513c 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -43,7 +43,7 @@ * 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 + * mounts a filesystem on /storage/sdcard. It will refuse to run if uid or * gid are zero. * * @@ -70,7 +70,7 @@ #define FUSE_UNKNOWN_INO 0xffffffff -#define MOUNT_POINT "/mnt/sdcard" +#define MOUNT_POINT "/storage/sdcard0" struct handle { struct node *node; @@ -912,7 +912,7 @@ void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *da out.major = FUSE_KERNEL_VERSION; out.minor = FUSE_KERNEL_MINOR_VERSION; out.max_readahead = req->max_readahead; - out.flags = FUSE_ATOMIC_O_TRUNC; + out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; out.max_background = 32; out.congestion_threshold = 32; out.max_write = 256 * 1024; @@ -941,7 +941,7 @@ void handle_fuse_requests(struct fuse *fuse) int len; for (;;) { - len = read(fuse->fd, req, 8192); + len = read(fuse->fd, req, sizeof(req)); if (len < 0) { if (errno == EINTR) continue; diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 9daeed3..be95e7c 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -55,7 +55,8 @@ TOOLS := \ nandread \ ionice \ touch \ - lsof + lsof \ + md5 ifeq ($(HAVE_SELINUX),true) @@ -83,6 +84,8 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := libcutils libc libusbhost +LOCAL_C_INCLUDES := bionic/libc/bionic + ifeq ($(HAVE_SELINUX),true) LOCAL_CFLAGS += -DHAVE_SELINUX diff --git a/toolbox/chown.c b/toolbox/chown.c index 7b24c52..92efee6 100644 --- a/toolbox/chown.c +++ b/toolbox/chown.c @@ -15,7 +15,7 @@ int chown_main(int argc, char **argv) int i; if (argc < 3) { - fprintf(stderr, "Usage: chown <USER>[.GROUP] <FILE1> [FILE2] ...\n"); + fprintf(stderr, "Usage: chown <USER>[:GROUP] <FILE1> [FILE2] ...\n"); return 10; } @@ -24,7 +24,9 @@ int chown_main(int argc, char **argv) char user[32]; char *group = NULL; strncpy(user, argv[1], sizeof(user)); - if ((group = strchr(user, '.')) != NULL) { + if ((group = strchr(user, ':')) != NULL) { + *group++ = '\0'; + } else if ((group = strchr(user, '.')) != NULL) { *group++ = '\0'; } diff --git a/toolbox/getevent.c b/toolbox/getevent.c index 352f6f9..5f5e16b 100644 --- a/toolbox/getevent.c +++ b/toolbox/getevent.c @@ -643,7 +643,7 @@ int getevent_main(int argc, char *argv[]) return 1; } if(get_time) { - printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec); + printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec); } if(print_device) printf("%s: ", device_names[i]); diff --git a/toolbox/hd.c b/toolbox/hd.c index da31245..0d2f96a 100644 --- a/toolbox/hd.c +++ b/toolbox/hd.c @@ -68,6 +68,8 @@ int hd_main(int argc, char *argv[]) if(count > 0 && base + count - filepos < read_len) read_len = base + count - filepos; res = read(fd, &buf, read_len); + if(res == 0) + break; for(i = 0; i < res; i++) { if((i & 15) == 0) { printf("%08x: ", filepos + i); @@ -80,7 +82,7 @@ int hd_main(int argc, char *argv[]) lsum = 0; } } - if(res <= 0) { + if(res < 0) { printf("Read error on %s, offset %d len %d, %s\n", argv[optind], filepos, read_len, strerror(errno)); return 1; } diff --git a/toolbox/kill.c b/toolbox/kill.c index 4d0e479..fa2f649 100644 --- a/toolbox/kill.c +++ b/toolbox/kill.c @@ -5,16 +5,129 @@ #include <sys/types.h> #include <signal.h> +static struct { + unsigned int number; + char *name; +} signals[] = { +#define _SIG(name) {SIG##name, #name} + /* Single Unix Specification signals */ + _SIG(ABRT), + _SIG(ALRM), + _SIG(FPE), + _SIG(HUP), + _SIG(ILL), + _SIG(INT), + _SIG(KILL), + _SIG(PIPE), + _SIG(QUIT), + _SIG(SEGV), + _SIG(TERM), + _SIG(USR1), + _SIG(USR2), + _SIG(CHLD), + _SIG(CONT), + _SIG(STOP), + _SIG(TSTP), + _SIG(TTIN), + _SIG(TTOU), + _SIG(BUS), + _SIG(POLL), + _SIG(PROF), + _SIG(SYS), + _SIG(TRAP), + _SIG(URG), + _SIG(VTALRM), + _SIG(XCPU), + _SIG(XFSZ), + /* non-SUS signals */ + _SIG(IO), + _SIG(PWR), +#ifdef SIGSTKFLT + _SIG(STKFLT), +#endif + _SIG(WINCH), +#undef _SIG +}; + +/* To indicate a matching signal was not found */ +static const unsigned int SENTINEL = (unsigned int) -1; + +void list_signals() +{ + unsigned int sorted_signals[_NSIG]; + unsigned int i; + unsigned int num; + + memset(sorted_signals, SENTINEL, sizeof(sorted_signals)); + + // Sort the signals + for (i = 0; i < sizeof(signals)/sizeof(signals[0]); i++) { + sorted_signals[signals[i].number] = i; + } + + num = 0; + for (i = 1; i < _NSIG; i++) { + unsigned int index = sorted_signals[i]; + if (index == SENTINEL) { + continue; + } + + fprintf(stderr, "%2d) SIG%-9s ", i, signals[index].name); + + if ((num++ % 4) == 3) { + fprintf(stderr, "\n"); + } + } + + if ((num % 4) == 3) { + fprintf(stderr, "\n"); + } +} + +unsigned int name_to_signal(const char* name) +{ + unsigned int i; + + for (i = 1; i < sizeof(signals) / sizeof(signals[0]); i++) { + if (!strcasecmp(name, signals[i].name)) { + return signals[i].number; + } + } + + return SENTINEL; +} + int kill_main(int argc, char **argv) { - int sig = SIGTERM; + unsigned int sig = SIGTERM; int result = 0; - + argc--; argv++; - if(argc >= 2 && argv[0][0] == '-'){ - sig = atoi(argv[0] + 1); + if (argc >= 1 && argv[0][0] == '-') { + char *endptr; + size_t arg_len = strlen(argv[0]); + if (arg_len < 2) { + fprintf(stderr, "invalid argument: -\n"); + return -1; + } + + char* arg = argv[0] + 1; + if (arg_len == 2 && *arg == 'l') { + list_signals(); + return 0; + } + + sig = strtol(arg, &endptr, 10); + if (*endptr != '\0') { + sig = name_to_signal(arg); + if (sig == SENTINEL) { + fprintf(stderr, "invalid signal name: %s\n", arg); + return -1; + } + } + argc--; argv++; } @@ -26,10 +139,10 @@ int kill_main(int argc, char **argv) result = err; fprintf(stderr, "could not kill pid %d: %s\n", pid, strerror(errno)); } - + argc--; argv++; } - + return result; } diff --git a/toolbox/lsof.c b/toolbox/lsof.c index 4e2f77a..376a642 100644 --- a/toolbox/lsof.c +++ b/toolbox/lsof.c @@ -178,8 +178,7 @@ void lsof_dumpinfo(pid_t pid) if (!stat(info.path, &pidstat)) { pw = getpwuid(pidstat.st_uid); if (pw) { - strncpy(info.user, pw->pw_name, USER_DISPLAY_MAX - 1); - info.user[USER_DISPLAY_MAX - 1] = '\0'; + strlcpy(info.user, pw->pw_name, sizeof(info.user)); } else { snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid); } @@ -194,18 +193,20 @@ void lsof_dumpinfo(pid_t pid) fprintf(stderr, "Couldn't read %s\n", info.path); return; } + char cmdline[PATH_MAX]; - if (read(fd, cmdline, sizeof(cmdline)) < 0) { + int numRead = read(fd, cmdline, sizeof(cmdline) - 1); + close(fd); + + if (numRead < 0) { fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno)); - close(fd); return; } - close(fd); - info.path[info.parent_length] = '\0'; + + cmdline[numRead] = '\0'; // We only want the basename of the cmdline - strncpy(info.cmdline, basename(cmdline), sizeof(info.cmdline)); - info.cmdline[sizeof(info.cmdline)-1] = '\0'; + strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline)); // Read each of these symlinks print_type("cwd", &info); diff --git a/toolbox/md5.c b/toolbox/md5.c new file mode 100644 index 0000000..2fb8b05 --- /dev/null +++ b/toolbox/md5.c @@ -0,0 +1,77 @@ +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <md5.h> + +/* When this was written, bionic's md5.h did not define this. */ +#ifndef MD5_DIGEST_LENGTH +#define MD5_DIGEST_LENGTH 16 +#endif + +static int usage() +{ + fprintf(stderr,"md5 file ...\n"); + return -1; +} + +static int do_md5(const char *path) +{ + unsigned int i; + int fd; + MD5_CTX md5_ctx; + unsigned char md5[MD5_DIGEST_LENGTH]; + + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr,"could not open %s, %s\n", path, strerror(errno)); + return -1; + } + + /* Note that bionic's MD5_* functions return void. */ + MD5_Init(&md5_ctx); + + while (1) { + char buf[4096]; + ssize_t rlen; + rlen = read(fd, buf, sizeof(buf)); + if (rlen == 0) + break; + else if (rlen < 0) { + (void)close(fd); + fprintf(stderr,"could not read %s, %s\n", path, strerror(errno)); + return -1; + } + MD5_Update(&md5_ctx, buf, rlen); + } + if (close(fd)) { + fprintf(stderr,"could not close %s, %s\n", path, strerror(errno)); + return -1; + } + + MD5_Final(md5, &md5_ctx); + + for (i = 0; i < (int)sizeof(md5); i++) + printf("%02x", md5[i]); + printf(" %s\n", path); + + return 0; +} + +int md5_main(int argc, char *argv[]) +{ + int i, ret = 0; + + if (argc < 2) + return usage(); + + /* loop over the file args */ + for (i = 1; i < argc; i++) { + if (do_md5(argv[i])) + ret = 1; + } + + return ret; +} diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c index ff9e844..6d78eb6 100644 --- a/toolbox/newfs_msdos.c +++ b/toolbox/newfs_msdos.c @@ -431,7 +431,8 @@ newfs_msdos_main(int argc, char *argv[]) bpb.spc = 8; else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */ bpb.spc = 16; - else if (bpb.bsec <= (1<<21)) /* 1G -> 16k */ + else if (bpb.bsec <= (1<<22)) /* 2G -> 16k, some versions of windows + require a minimum of 65527 clusters */ bpb.spc = 32; else bpb.spc = 64; /* otherwise 32k */ diff --git a/toolbox/powerd.c b/toolbox/powerd.c deleted file mode 100644 index 1f29a8b..0000000 --- a/toolbox/powerd.c +++ /dev/null @@ -1,441 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <fcntl.h> -#include <string.h> -#include <errno.h> -#include <time.h> -#include <sys/select.h> -#include <sys/inotify.h> - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -//#include <linux/input.h> // this does not compile - -// from <linux/input.h> - -struct input_event { - struct timeval time; - __u16 type; - __u16 code; - __s32 value; -}; - -#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ -#define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ -#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ -#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ - -#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ -#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ -#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ - -#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */ -#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ -#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ -#define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) /* get all switch states */ - -#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ -#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) /* get abs value/limits */ -#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) /* set abs value/limits */ - -#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ -#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ -#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ - -#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ - -/* - * Event types - */ - -#define EV_SYN 0x00 -#define EV_KEY 0x01 -#define EV_REL 0x02 -#define EV_ABS 0x03 -#define EV_MSC 0x04 -#define EV_SW 0x05 -#define EV_LED 0x11 -#define EV_SND 0x12 -#define EV_REP 0x14 -#define EV_FF 0x15 -#define EV_PWR 0x16 -#define EV_FF_STATUS 0x17 -#define EV_MAX 0x1f - -#define KEY_POWER 116 -#define KEY_SLEEP 142 -#define SW_0 0x00 - -// end <linux/input.h> - -struct notify_entry { - int id; - int (*handler)(struct notify_entry *entry, struct inotify_event *event); - const char *filename; -}; - -int charging_state_notify_handler(struct notify_entry *entry, struct inotify_event *event) -{ - static int state = -1; - int last_state; - char buf[40]; - int read_len; - int fd; - - last_state = state; - fd = open(entry->filename, O_RDONLY); - read_len = read(fd, buf, sizeof(buf)); - if(read_len > 0) { - //printf("charging_state_notify_handler: \"%s\"\n", buf); - state = !(strncmp(buf, "Unknown", 7) == 0 - || strncmp(buf, "Discharging", 11) == 0); - } - close(fd); - //printf("charging_state_notify_handler: %d -> %d\n", last_state, state); - return state > last_state; -} - -struct notify_entry watched_files[] = { - { - .filename = "/sys/android_power/charging_state", - .handler = charging_state_notify_handler - } -}; - -int call_notify_handler(struct inotify_event *event) -{ - unsigned int start, i; - start = event->wd - watched_files[0].id; - if(start >= ARRAY_SIZE(watched_files)) - start = 0; - //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); - for(i = start; i < ARRAY_SIZE(watched_files); i++) { - if(event->wd == watched_files[i].id) { - if(watched_files[i].handler) { - return watched_files[i].handler(&watched_files[i], event); - } - return 1; - } - } - for(i = 0; i < start; i++) { - if(event->wd == watched_files[i].id) { - if(watched_files[i].handler) { - return watched_files[i].handler(&watched_files[i], event); - } - return 1; - } - } - return 0; -} - -int handle_inotify_event(int nfd) -{ - int res; - int wake_up = 0; - struct inotify_event *event; - char event_buf[512]; - int event_pos = 0; - - res = read(nfd, event_buf, sizeof(event_buf)); - if(res < (int)sizeof(*event)) { - if(errno == EINTR) - return 0; - fprintf(stderr, "could not get event, %s\n", strerror(errno)); - return 0; - } - printf("got %d bytes of event information\n", res); - while(res >= (int)sizeof(*event)) { - int event_size; - event = (struct inotify_event *)(event_buf + event_pos); - wake_up |= call_notify_handler(event); - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } - return wake_up; -} - -int powerd_main(int argc, char *argv[]) -{ - int c; - unsigned int i; - int res; - struct timeval tv; - int eventfd; - int notifyfd; - int powerfd; - int powerfd_is_sleep; - int user_activity_fd; - int acquire_partial_wake_lock_fd; - int acquire_full_wake_lock_fd; - int release_wake_lock_fd; - char *eventdev = "/dev/input/event0"; - const char *android_sleepdev = "/sys/android_power/request_sleep"; - const char *android_autooff_dev = "/sys/android_power/auto_off_timeout"; - const char *android_user_activity_dev = "/sys/android_power/last_user_activity"; - const char *android_acquire_partial_wake_lock_dev = "/sys/android_power/acquire_partial_wake_lock"; - const char *android_acquire_full_wake_lock_dev = "/sys/android_power/acquire_full_wake_lock"; - const char *android_release_wake_lock_dev = "/sys/android_power/release_wake_lock"; - const char *powerdev = "/sys/power/state"; - const char suspendstring[] = "standby"; - const char wakelockstring[] = "powerd"; - fd_set rfds; - struct input_event event; - struct input_event light_event; - struct input_event light_event2; - int gotkey = 1; - time_t idle_time = 5; - const char *idle_time_string = "5"; - time_t lcd_light_time = 0; - time_t key_light_time = 0; - int verbose = 1; - int event_sleep = 0; - int got_power_key_down = 0; - struct timeval power_key_down_time = { 0, 0 }; - - light_event.type = EV_LED; - light_event.code = 4; // bright lcd backlight - light_event.value = 0; // light off -- sleep after timeout - - light_event2.type = EV_LED; - light_event2.code = 8; // keyboard backlight - light_event2.value = 0; // light off -- sleep after timeout - - do { - c = getopt(argc, argv, "e:ni:vql:k:"); - if (c == EOF) - break; - switch (c) { - case 'e': - eventdev = optarg; - break; - case 'n': - gotkey = 0; - break; - case 'i': - idle_time = atoi(optarg); - idle_time_string = optarg; - break; - case 'v': - verbose = 2; - break; - case 'q': - verbose = 0; - break; - case 'l': - lcd_light_time = atoi(optarg); - break; - case 'k': - key_light_time = atoi(optarg); - break; - case '?': - fprintf(stderr, "%s: invalid option -%c\n", - argv[0], optopt); - exit(1); - } - } while (1); - if(optind != argc) { - fprintf(stderr,"%s [-e eventdev]\n", argv[0]); - return 1; - } - - eventfd = open(eventdev, O_RDWR | O_NONBLOCK); - if(eventfd < 0) { - fprintf(stderr, "could not open %s, %s\n", eventdev, strerror(errno)); - return 1; - } - if(key_light_time >= lcd_light_time) { - lcd_light_time = key_light_time + 1; - fprintf(stderr,"lcd bright backlight time must be longer than keyboard backlight time.\n" - "Setting lcd bright backlight time to %ld seconds\n", lcd_light_time); - } - - user_activity_fd = open(android_user_activity_dev, O_RDWR); - if(user_activity_fd >= 0) { - int auto_off_fd = open(android_autooff_dev, O_RDWR); - write(auto_off_fd, idle_time_string, strlen(idle_time_string)); - close(auto_off_fd); - } - - powerfd = open(android_sleepdev, O_RDWR); - if(powerfd >= 0) { - powerfd_is_sleep = 1; - if(verbose > 0) - printf("Using android sleep dev: %s\n", android_sleepdev); - } - else { - powerfd_is_sleep = 0; - powerfd = open(powerdev, O_RDWR); - if(powerfd >= 0) { - if(verbose > 0) - printf("Using linux power dev: %s\n", powerdev); - } - } - if(powerfd < 0) { - fprintf(stderr, "could not open %s, %s\n", powerdev, strerror(errno)); - return 1; - } - - notifyfd = inotify_init(); - if(notifyfd < 0) { - fprintf(stderr, "inotify_init failed, %s\n", strerror(errno)); - return 1; - } - fcntl(notifyfd, F_SETFL, O_NONBLOCK | fcntl(notifyfd, F_GETFL)); - for(i = 0; i < ARRAY_SIZE(watched_files); i++) { - watched_files[i].id = inotify_add_watch(notifyfd, watched_files[i].filename, IN_MODIFY); - printf("Watching %s, id %d\n", watched_files[i].filename, watched_files[i].id); - } - - acquire_partial_wake_lock_fd = open(android_acquire_partial_wake_lock_dev, O_RDWR); - acquire_full_wake_lock_fd = open(android_acquire_full_wake_lock_dev, O_RDWR); - release_wake_lock_fd = open(android_release_wake_lock_dev, O_RDWR); - - if(user_activity_fd >= 0) { - idle_time = 60*60*24; // driver handles real timeout - } - if(gotkey) { - tv.tv_sec = idle_time; - tv.tv_usec = 0; - } - else { - tv.tv_sec = 0; - tv.tv_usec = 500000; - } - - while(1) { - FD_ZERO(&rfds); - //FD_SET(0, &rfds); - FD_SET(eventfd, &rfds); - FD_SET(notifyfd, &rfds); - res = select(((notifyfd > eventfd) ? notifyfd : eventfd) + 1, &rfds, NULL, NULL, &tv); - if(res < 0) { - fprintf(stderr, "select failed, %s\n", strerror(errno)); - return 1; - } - if(res == 0) { - if(light_event2.value == 1) - goto light2_off; - if(light_event.value == 1) - goto light_off; - if(user_activity_fd < 0) { - if(gotkey && verbose > 0) - printf("Idle - sleep\n"); - if(!gotkey && verbose > 1) - printf("Reenter sleep\n"); - goto sleep; - } - else { - tv.tv_sec = 60*60*24; - tv.tv_usec = 0; - } - } - if(res > 0) { - //if(FD_ISSET(0, &rfds)) { - // printf("goto data on stdin quit\n"); - // return 0; - //} - if(FD_ISSET(notifyfd, &rfds)) { - write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - if(handle_inotify_event(notifyfd) > 0) { - write(acquire_full_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - } - write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - } - if(FD_ISSET(eventfd, &rfds)) { - write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - res = read(eventfd, &event, sizeof(event)); - if(res < (int)sizeof(event)) { - fprintf(stderr, "could not get event\n"); - write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - return 1; - } - if(event.type == EV_PWR && event.code == KEY_SLEEP) { - event_sleep = event.value; - } - if(event.type == EV_KEY || (event.type == EV_SW && event.code == SW_0 && event.value == 1)) { - gotkey = 1; - if(user_activity_fd >= 0) { - char buf[32]; - int len; - len = sprintf(buf, "%ld%06lu000", event.time.tv_sec, event.time.tv_usec); - write(user_activity_fd, buf, len); - } - if(lcd_light_time | key_light_time) { - tv.tv_sec = key_light_time; - light_event.value = 1; - write(eventfd, &light_event, sizeof(light_event)); - light_event2.value = 1; - write(eventfd, &light_event2, sizeof(light_event2)); - } - else { - tv.tv_sec = idle_time; - } - tv.tv_usec = 0; - if(verbose > 1) - printf("got %s %s %d%s\n", event.type == EV_KEY ? "key" : "switch", event.value ? "down" : "up", event.code, event_sleep ? " from sleep" : ""); - if(event.code == KEY_POWER) { - if(event.value == 0) { - int tmp_got_power_key_down = got_power_key_down; - got_power_key_down = 0; - if(tmp_got_power_key_down) { - // power key released - if(verbose > 0) - printf("Power key released - sleep\n"); - write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - goto sleep; - } - } - else if(event_sleep == 0) { - got_power_key_down = 1; - power_key_down_time = event.time; - } - } - } - if(event.type == EV_SW && event.code == SW_0 && event.value == 0) { - if(verbose > 0) - printf("Flip closed - sleep\n"); - power_key_down_time = event.time; - write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - goto sleep; - } - write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1); - } - } - if(0) { -light_off: - light_event.value = 0; - write(eventfd, &light_event, sizeof(light_event)); - tv.tv_sec = idle_time - lcd_light_time; - } - if(0) { -light2_off: - light_event2.value = 0; - write(eventfd, &light_event2, sizeof(light_event2)); - tv.tv_sec = lcd_light_time - key_light_time; - } - if(0) { -sleep: - if(light_event.value == 1) { - light_event.value = 0; - write(eventfd, &light_event, sizeof(light_event)); - light_event2.value = 0; - write(eventfd, &light_event2, sizeof(light_event2)); - tv.tv_sec = idle_time - lcd_light_time; - } - if(powerfd_is_sleep) { - char buf[32]; - int len; - len = sprintf(buf, "%ld%06lu000", power_key_down_time.tv_sec, power_key_down_time.tv_usec); - write(powerfd, buf, len); - } - else - write(powerfd, suspendstring, sizeof(suspendstring) - 1); - gotkey = 0; - tv.tv_sec = 0; - tv.tv_usec = 500000; - } - } - - return 0; -} diff --git a/toolbox/ps.c b/toolbox/ps.c index 7c3de4a..7c35ccb 100644 --- a/toolbox/ps.c +++ b/toolbox/ps.c @@ -167,14 +167,8 @@ static int ps_line(int pid, int tid, char *namefilter) SchedPolicy p; if (get_sched_policy(pid, &p) < 0) printf(" un "); - else { - if (p == SP_BACKGROUND) - printf(" bg "); - else if (p == SP_FOREGROUND) - printf(" fg "); - else - printf(" er "); - } + else + printf(" %.2s ", get_sched_policy_name(p)); } printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name); if(display_flags&SHOW_TIME) diff --git a/toolbox/rmmod.c b/toolbox/rmmod.c index 25257cc..c7e0d6a 100644 --- a/toolbox/rmmod.c +++ b/toolbox/rmmod.c @@ -10,7 +10,7 @@ extern int delete_module(const char *, unsigned int); int rmmod_main(int argc, char **argv) { - int ret; + int ret, i; char *modname, *dot; /* make sure we've got an argument */ @@ -31,6 +31,15 @@ int rmmod_main(int argc, char **argv) if (dot) *dot = '\0'; + /* Replace "-" with "_". This would keep rmmod + * compatible with module-init-tools version of + * rmmod + */ + for (i = 0; modname[i] != '\0'; i++) { + if (modname[i] == '-') + modname[i] = '_'; + } + /* pass it to the kernel */ ret = delete_module(modname, O_NONBLOCK | O_EXCL); if (ret != 0) { diff --git a/toolbox/top.c b/toolbox/top.c index 999c8e1..7642522 100644 --- a/toolbox/top.c +++ b/toolbox/top.c @@ -48,6 +48,7 @@ struct cpu_info { #define PROC_NAME_LEN 64 #define THREAD_NAME_LEN 32 +#define POLICY_NAME_LEN 4 struct proc_info { struct proc_info *next; @@ -67,7 +68,7 @@ struct proc_info { long rss; int prs; int num_threads; - char policy[32]; + char policy[POLICY_NAME_LEN]; }; struct proc_list { @@ -381,14 +382,10 @@ static int read_cmdline(char *filename, struct proc_info *proc) { static void read_policy(int pid, struct proc_info *proc) { SchedPolicy p; if (get_sched_policy(pid, &p) < 0) - strcpy(proc->policy, "unk"); + strlcpy(proc->policy, "unk", POLICY_NAME_LEN); else { - if (p == SP_BACKGROUND) - strcpy(proc->policy, "bg"); - else if (p == SP_FOREGROUND) - strcpy(proc->policy, "fg"); - else - strcpy(proc->policy, "er"); + strlcpy(proc->policy, get_sched_policy_name(p), POLICY_NAME_LEN); + proc->policy[2] = '\0'; } } |