diff options
382 files changed, 29916 insertions, 20914 deletions
diff --git a/adb/Android.mk b/adb/Android.mk index 7744d2b..6cd82ec 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -16,7 +16,7 @@ EXTRA_SRCS := ifeq ($(HOST_OS),linux) USB_SRCS := usb_linux.c EXTRA_SRCS := get_my_path_linux.c - LOCAL_LDLIBS += -lrt -lncurses -lpthread + LOCAL_LDLIBS += -lrt -ldl -lpthread endif ifeq ($(HOST_OS),darwin) @@ -33,16 +33,16 @@ endif ifeq ($(HOST_OS),windows) USB_SRCS := usb_windows.c - EXTRA_SRCS := get_my_path_windows.c + EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c EXTRA_STATIC_LIBS := AdbWinApi ifneq ($(strip $(USE_CYGWIN)),) # Pure cygwin case - LOCAL_LDLIBS += -lpthread + LOCAL_LDLIBS += -lpthread -lgdi32 LOCAL_C_INCLUDES += /usr/include/w32api/ddk endif ifneq ($(strip $(USE_MINGW)),) # MinGW under Linux case - LOCAL_LDLIBS += -lws2_32 + LOCAL_LDLIBS += -lws2_32 -lgdi32 USE_SYSDEPS_WIN32 := 1 LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk endif @@ -57,6 +57,7 @@ LOCAL_SRC_FILES := \ transport_usb.c \ commandline.c \ adb_client.c \ + adb_auth_host.c \ sockets.c \ services.c \ file_sync_client.c \ @@ -65,6 +66,7 @@ LOCAL_SRC_FILES := \ utils.c \ usb_vendors.c +LOCAL_C_INCLUDES += external/openssl/include ifneq ($(USE_SYSDEPS_WIN32),) LOCAL_SRC_FILES += sysdeps_win32.c @@ -75,15 +77,16 @@ endif LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := adb +LOCAL_MODULE_TAGS := debug -LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS) +LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS) ifeq ($(USE_SYSDEPS_WIN32),) LOCAL_STATIC_LIBRARIES += libcutils endif include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),windows) $(LOCAL_INSTALLED_MODULE): \ @@ -95,16 +98,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 := \ @@ -114,6 +107,7 @@ LOCAL_SRC_FILES := \ transport.c \ transport_local.c \ transport_usb.c \ + adb_auth_client.c \ sockets.c \ services.c \ file_sync_service.c \ @@ -127,10 +121,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 @@ -139,18 +131,16 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt include $(BUILD_EXECUTABLE) -endif - # adb host tool for device-as-host # ========================================================= ifneq ($(SDK_ONLY),true) include $(CLEAR_VARS) -LOCAL_LDLIBS := -lrt -lncurses -lpthread +LOCAL_LDLIBS := -lrt -ldl -lpthread LOCAL_SRC_FILES := \ adb.c \ @@ -160,6 +150,7 @@ LOCAL_SRC_FILES := \ transport_usb.c \ commandline.c \ adb_client.c \ + adb_auth_host.c \ sockets.c \ services.c \ file_sync_client.c \ @@ -179,9 +170,13 @@ LOCAL_CFLAGS := \ -D_XOPEN_SOURCE \ -D_GNU_SOURCE +LOCAL_C_INCLUDES += external/openssl/include + LOCAL_MODULE := adb LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils +LOCAL_SHARED_LIBRARIES := libcrypto + include $(BUILD_EXECUTABLE) endif diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index be4d50b..b53bc44 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -17,8 +17,10 @@ host:kill upgrade. host:devices +host:devices-l Ask to return the list of available Android devices and their - state. After the OKAY, this is followed by a 4-byte hex len, + state. devices-l includes the device paths in the state. + After the OKAY, this is followed by a 4-byte hex len, and a string that will be dumped as-is by the client, then the connection is closed @@ -88,6 +90,9 @@ host:<request> Returns the serial number of the corresponding device/emulator. Note that emulator serial numbers are of the form "emulator-5554" +<host-prefix>:get-devpath + Returns the device path of the corresponding device/emulator. + <host-prefix>:get-state Returns the state of a given device as a string. @@ -112,7 +117,34 @@ host:<request> or even any one of the local services described below. +<host-prefix>:forward:norebind:<local>;<remote> + Same as <host-prefix>:forward:<local>;<remote> except that it will + fail it there is already a forward connection from <local>. + + Used to implement 'adb forward --no-rebind <local> <remote>' + +<host-prefix>:killforward:<local> + Remove any existing forward local connection from <local>. + This is used to implement 'adb forward --remove <local>' + +<host-prefix>:killforward-all + Remove all forward network connections. + This is used to implement 'adb forward --remove-all'. + +<host-prefix>:list-forward + List all existing forward connections from this server. + This returns something that looks like the following: + + <hex4>: The length of the payload, as 4 hexadecimal chars. + <payload>: A series of lines of the following format: + + <serial> " " <local> " " <remote> "\n" + + Where <serial> is a device serial number. + <local> is the host-specific endpoint (e.g. tcp:9000). + <remote> is the device-specific endpoint. + Used to implement 'adb forward --list'. LOCAL SERVICES: @@ -21,17 +21,23 @@ #include <ctype.h> #include <stdarg.h> #include <errno.h> +#include <stddef.h> #include <string.h> #include <time.h> #include <sys/time.h> +#include <stdint.h> #include "sysdeps.h" #include "adb.h" +#include "adb_auth.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #if !ADB_HOST #include <private/android_filesystem_config.h> -#include <linux/capability.h> +#include <sys/capability.h> #include <linux/prctl.h> +#include <sys/mount.h> #else #include "usb_vendors.h" #endif @@ -41,8 +47,13 @@ ADB_MUTEX_DEFINE( D_lock ); #endif int HOST = 0; +int gListenAll = 0; + +static int auth_enabled = 0; +#if !ADB_HOST static const char *adb_device_banner = "device"; +#endif void fatal(const char *fmt, ...) { @@ -94,6 +105,7 @@ void adb_trace_init(void) { "transport", TRACE_TRANSPORT }, { "jdwp", TRACE_JDWP }, { "services", TRACE_SERVICES }, + { "auth", TRACE_AUTH }, { NULL, 0 } }; @@ -131,6 +143,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) { @@ -145,19 +209,21 @@ void put_apacket(apacket *p) free(p); } -void handle_online(void) +void handle_online(atransport *t) { D("adb: online\n"); + t->online = 1; } void handle_offline(atransport *t) { D("adb: offline\n"); //Close the associated usb + t->online = 0; run_transport_disconnects(t); } -#if TRACE_PACKETS +#if DEBUG_PACKETS #define DUMPMAX 32 void print_packet(const char *label, apacket *p) { @@ -172,6 +238,7 @@ void print_packet(const char *label, apacket *p) case A_OKAY: tag = "OKAY"; break; case A_CLSE: tag = "CLSE"; break; case A_WRTE: tag = "WRTE"; break; + case A_AUTH: tag = "AUTH"; break; default: tag = "????"; break; } @@ -193,7 +260,7 @@ void print_packet(const char *label, apacket *p) } x++; } - fprintf(stderr, tag); + fputs(tag, stderr); } #endif @@ -217,6 +284,36 @@ static void send_close(unsigned local, unsigned remote, atransport *t) send_packet(p, t); } +static size_t fill_connect_data(char *buf, size_t bufsize) +{ +#if ADB_HOST + return snprintf(buf, bufsize, "host::") + 1; +#else + static const char *cnxn_props[] = { + "ro.product.name", + "ro.product.model", + "ro.product.device", + }; + static const int num_cnxn_props = ARRAY_SIZE(cnxn_props); + int i; + size_t remaining = bufsize; + size_t len; + + len = snprintf(buf, remaining, "%s::", adb_device_banner); + remaining -= len; + buf += len; + for (i = 0; i < num_cnxn_props; i++) { + char value[PROPERTY_VALUE_MAX]; + property_get(cnxn_props[i], value, ""); + len = snprintf(buf, remaining, "%s=%s;", cnxn_props[i], value); + remaining -= len; + buf += len; + } + + return bufsize - remaining + 1; +#endif +} + static void send_connect(atransport *t) { D("Calling send_connect \n"); @@ -224,15 +321,73 @@ static void send_connect(atransport *t) cp->msg.command = A_CNXN; cp->msg.arg0 = A_VERSION; cp->msg.arg1 = MAX_PAYLOAD; - snprintf((char*) cp->data, sizeof cp->data, "%s::", - HOST ? "host" : adb_device_banner); - cp->msg.data_length = strlen((char*) cp->data) + 1; + cp->msg.data_length = fill_connect_data((char *)cp->data, + sizeof(cp->data)); send_packet(cp, t); -#if ADB_HOST - /* XXX why sleep here? */ - // allow the device some time to respond to the connect message - adb_sleep_ms(1000); -#endif +} + +static void send_auth_request(atransport *t) +{ + D("Calling send_auth_request\n"); + apacket *p; + int ret; + + ret = adb_auth_generate_token(t->token, sizeof(t->token)); + if (ret != sizeof(t->token)) { + D("Error generating token ret=%d\n", ret); + return; + } + + p = get_apacket(); + memcpy(p->data, t->token, ret); + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_TOKEN; + p->msg.data_length = ret; + send_packet(p, t); +} + +static void send_auth_response(uint8_t *token, size_t token_size, atransport *t) +{ + D("Calling send_auth_response\n"); + apacket *p = get_apacket(); + int ret; + + ret = adb_auth_sign(t->key, token, token_size, p->data); + if (!ret) { + D("Error signing the token\n"); + put_apacket(p); + return; + } + + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_SIGNATURE; + p->msg.data_length = ret; + send_packet(p, t); +} + +static void send_auth_publickey(atransport *t) +{ + D("Calling send_auth_publickey\n"); + apacket *p = get_apacket(); + int ret; + + ret = adb_auth_get_userkey(p->data, sizeof(p->data)); + if (!ret) { + D("Failed to get user public key\n"); + put_apacket(p); + return; + } + + p->msg.command = A_AUTH; + p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY; + p->msg.data_length = ret; + send_packet(p, t); +} + +void adb_auth_verified(atransport *t) +{ + handle_online(t); + send_connect(t); } static char *connection_state_name(atransport *t) @@ -246,6 +401,10 @@ static char *connection_state_name(atransport *t) return "bootloader"; case CS_DEVICE: return "device"; + case CS_RECOVERY: + return "recovery"; + case CS_SIDELOAD: + return "sideload"; case CS_OFFLINE: return "offline"; default: @@ -253,29 +412,56 @@ static char *connection_state_name(atransport *t) } } +/* qual_overwrite is used to overwrite a qualifier string. dst is a + * pointer to a char pointer. It is assumed that if *dst is non-NULL, it + * was malloc'ed and needs to freed. *dst will be set to a dup of src. + */ +static void qual_overwrite(char **dst, const char *src) +{ + if (!dst) + return; + + free(*dst); + *dst = NULL; + + if (!src || !*src) + return; + + *dst = strdup(src); +} + void parse_banner(char *banner, atransport *t) { - char *type, *product, *end; + static const char *prop_seps = ";"; + static const char key_val_sep = '='; + char *cp; + char *type; D("parse_banner: %s\n", banner); type = banner; - product = strchr(type, ':'); - if(product) { - *product++ = 0; - } else { - product = ""; - } - - /* remove trailing ':' */ - end = strchr(product, ':'); - if(end) *end = 0; - - /* save product name in device structure */ - if (t->product == NULL) { - t->product = strdup(product); - } else if (strcmp(product, t->product) != 0) { - free(t->product); - t->product = strdup(product); + cp = strchr(type, ':'); + if (cp) { + *cp++ = 0; + /* Nothing is done with second field. */ + cp = strchr(cp, ':'); + if (cp) { + char *save; + char *key; + key = adb_strtok_r(cp + 1, prop_seps, &save); + while (key) { + cp = strchr(key, key_val_sep); + if (cp) { + *cp++ = '\0'; + if (!strcmp(key, "ro.product.name")) + qual_overwrite(&t->product, cp); + else if (!strcmp(key, "ro.product.model")) + qual_overwrite(&t->model, cp); + else if (!strcmp(key, "ro.product.device")) + qual_overwrite(&t->device, cp); + } + key = adb_strtok_r(NULL, prop_seps, &save); + } + } } if(!strcmp(type, "bootloader")){ @@ -299,6 +485,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; } @@ -330,13 +523,42 @@ void handle_packet(apacket *p, atransport *t) t->connection_state = CS_OFFLINE; handle_offline(t); } + parse_banner((char*) p->data, t); - handle_online(); - if(!HOST) send_connect(t); + + if (HOST || !auth_enabled) { + handle_online(t); + if(!HOST) send_connect(t); + } else { + send_auth_request(t); + } + break; + + case A_AUTH: + if (p->msg.arg0 == ADB_AUTH_TOKEN) { + t->key = adb_auth_nextkey(t->key); + if (t->key) { + send_auth_response(p->data, p->msg.data_length, t); + } else { + /* No more private keys to try, send the public key */ + send_auth_publickey(t); + } + } else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) { + if (adb_auth_verify(t->token, p->data, p->msg.data_length)) { + adb_auth_verified(t); + t->failed_auth_attempts = 0; + } else { + if (t->failed_auth_attempts++ > 10) + adb_sleep_ms(1000); + send_auth_request(t); + } + } else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) { + adb_auth_confirm_key(p->data, p->msg.data_length, t); + } break; case A_OPEN: /* OPEN(local-id, 0, "destination") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { char *name = (char*) p->data; name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0; s = create_local_service_socket(name); @@ -352,7 +574,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_OKAY: /* READY(local-id, remote-id, "") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { if(s->peer == 0) { s->peer = create_remote_socket(p->msg.arg0, t); @@ -364,7 +586,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_CLSE: /* CLOSE(local-id, remote-id, "") */ - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { s->close(s); } @@ -372,7 +594,7 @@ void handle_packet(apacket *p, atransport *t) break; case A_WRTE: - if(t->connection_state != CS_OFFLINE) { + if (t->online) { if((s = find_local_socket(p->msg.arg1))) { unsigned rid = p->msg.arg0; p->len = p->msg.data_length; @@ -485,7 +707,13 @@ int local_name_to_fd(const char *name) if(!strncmp("tcp:", name, 4)){ int ret; port = atoi(name + 4); - ret = socket_loopback_server(port, SOCK_STREAM); + + if (gListenAll > 0) { + ret = socket_inaddr_any_server(port, SOCK_STREAM); + } else { + ret = socket_loopback_server(port, SOCK_STREAM); + } + return ret; } #ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */ @@ -506,24 +734,90 @@ int local_name_to_fd(const char *name) return -1; } -static int remove_listener(const char *local_name, const char *connect_to, atransport* transport) +// Write a single line describing a listener to a user-provided buffer. +// Appends a trailing zero, even in case of truncation, but the function +// returns the full line length. +// If |buffer| is NULL, does not write but returns required size. +static int format_listener(alistener* l, char* buffer, size_t buffer_len) { + // Format is simply: + // + // <device-serial> " " <local-name> " " <remote-name> "\n" + // + int local_len = strlen(l->local_name); + int connect_len = strlen(l->connect_to); + int serial_len = strlen(l->transport->serial); + + if (buffer != NULL) { + snprintf(buffer, buffer_len, "%s %s %s\n", + l->transport->serial, l->local_name, l->connect_to); + } + // NOTE: snprintf() on Windows returns -1 in case of truncation, so + // return the computed line length instead. + return local_len + connect_len + serial_len + 3; +} + +// Write the list of current listeners (network redirections) into a +// user-provided buffer. Appends a trailing zero, even in case of +// trunctaion, but return the full size in bytes. +// If |buffer| is NULL, does not write but returns required size. +static int format_listeners(char* buf, size_t buflen) +{ + alistener* l; + int result = 0; + for (l = listener_list.next; l != &listener_list; l = l->next) { + // Ignore special listeners like those for *smartsocket* + if (l->connect_to[0] == '*') + continue; + int len = format_listener(l, buf, buflen); + // Ensure there is space for the trailing zero. + result += len; + if (buf != NULL) { + buf += len; + buflen -= len; + if (buflen <= 0) + break; + } + } + return result; +} + +static int remove_listener(const char *local_name, atransport* transport) { alistener *l; for (l = listener_list.next; l != &listener_list; l = l->next) { - if (!strcmp(local_name, l->local_name) && - !strcmp(connect_to, l->connect_to) && - l->transport && l->transport == transport) { - - listener_disconnect(l, transport); + if (!strcmp(local_name, l->local_name)) { + listener_disconnect(l, l->transport); return 0; } } - return -1; } -static int install_listener(const char *local_name, const char *connect_to, atransport* transport) +static void remove_all_listeners(void) +{ + alistener *l, *l_next; + for (l = listener_list.next; l != &listener_list; l = l_next) { + l_next = l->next; + // Never remove smart sockets. + if (l->connect_to[0] == '*') + continue; + listener_disconnect(l, l->transport); + } +} + +// error/status codes for install_listener. +typedef enum { + INSTALL_STATUS_OK = 0, + INSTALL_STATUS_INTERNAL_ERROR = -1, + INSTALL_STATUS_CANNOT_BIND = -2, + INSTALL_STATUS_CANNOT_REBIND = -3, +} install_status_t; + +static install_status_t install_listener(const char *local_name, + const char *connect_to, + atransport* transport, + int no_rebind) { alistener *l; @@ -535,12 +829,17 @@ static int install_listener(const char *local_name, const char *connect_to, atra /* can't repurpose a smartsocket */ if(l->connect_to[0] == '*') { - return -1; + return INSTALL_STATUS_INTERNAL_ERROR; + } + + /* can't repurpose a listener if 'no_rebind' is true */ + if (no_rebind) { + return INSTALL_STATUS_CANNOT_REBIND; } cto = strdup(connect_to); if(cto == 0) { - return -1; + return INSTALL_STATUS_INTERNAL_ERROR; } //printf("rebinding '%s' to '%s'\n", local_name, connect_to); @@ -551,7 +850,7 @@ static int install_listener(const char *local_name, const char *connect_to, atra l->transport = transport; add_transport_disconnect(l->transport, &l->disconnect); } - return 0; + return INSTALL_STATUS_OK; } } @@ -588,11 +887,11 @@ static int install_listener(const char *local_name, const char *connect_to, atra l->disconnect.func = listener_disconnect; add_transport_disconnect(transport, &l->disconnect); } - return 0; + return INSTALL_STATUS_OK; nomem: fatal("cannot allocate listener"); - return 0; + return INSTALL_STATUS_INTERNAL_ERROR; } #ifdef HAVE_WIN32_PROC @@ -697,6 +996,7 @@ int launch_server(int server_port) /* message since the pipe handles must be inheritable, we use a */ /* security attribute */ HANDLE pipe_read, pipe_write; + HANDLE stdout_handle, stderr_handle; SECURITY_ATTRIBUTES sa; STARTUPINFO startup; PROCESS_INFORMATION pinfo; @@ -716,6 +1016,26 @@ int launch_server(int server_port) SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 ); + /* Some programs want to launch an adb command and collect its output by + * calling CreateProcess with inheritable stdout/stderr handles, then + * using read() to get its output. When this happens, the stdout/stderr + * handles passed to the adb client process will also be inheritable. + * When starting the adb server here, care must be taken to reset them + * to non-inheritable. + * Otherwise, something bad happens: even if the adb command completes, + * the calling process is stuck while read()-ing from the stdout/stderr + * descriptors, because they're connected to corresponding handles in the + * adb server process (even if the latter never uses/writes to them). + */ + stdout_handle = GetStdHandle( STD_OUTPUT_HANDLE ); + stderr_handle = GetStdHandle( STD_ERROR_HANDLE ); + if (stdout_handle != INVALID_HANDLE_VALUE) { + SetHandleInformation( stdout_handle, HANDLE_FLAG_INHERIT, 0 ); + } + if (stderr_handle != INVALID_HANDLE_VALUE) { + SetHandleInformation( stderr_handle, HANDLE_FLAG_INHERIT, 0 ); + } + ZeroMemory( &startup, sizeof(startup) ); startup.cb = sizeof(startup); startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE ); @@ -792,8 +1112,10 @@ int launch_server(int server_port) dup2(fd[1], STDERR_FILENO); adb_close(fd[1]); + char str_port[30]; + snprintf(str_port, sizeof(str_port), "%d", server_port); // child process - int result = execl(path, "adb", "fork-server", "server", NULL); + int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL); // this should not return fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); } else { @@ -835,12 +1157,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); @@ -853,46 +1210,39 @@ int adb_main(int is_daemon, int server_port) init_transport_registration(); - #if ADB_HOST HOST = 1; usb_vendors_init(); usb_init(); local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); + adb_auth_init(); char local_name[30]; build_local_name(local_name, sizeof(local_name), server_port); - if(install_listener(local_name, "*smartsocket*", NULL)) { + if(install_listener(local_name, "*smartsocket*", NULL, 0)) { 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; - } - } - } + property_get("ro.adb.secure", value, "0"); + auth_enabled = !strcmp(value, "1"); + if (auth_enabled) + adb_auth_init(); + + // Our external storage path may be different than apps, since + // we aren't able to bind mount after dropping root. + const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE"); + if (NULL != adb_external_storage) { + setenv("EXTERNAL_STORAGE", adb_external_storage, 1); + } else { + D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE" + " unchanged.\n"); } /* 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; + struct __user_cap_data_struct cap[2]; if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) { exit(1); @@ -905,13 +1255,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); } @@ -924,40 +1275,48 @@ int adb_main(int is_daemon, int server_port) exit(1); } + memset(&header, 0, sizeof(header)); + memset(cap, 0, sizeof(cap)); + /* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */ - header.version = _LINUX_CAPABILITY_VERSION; + header.version = _LINUX_CAPABILITY_VERSION_3; header.pid = 0; - cap.effective = cap.permitted = (1 << CAP_SYS_BOOT); - cap.inheritable = 0; - capset(&header, &cap); + cap[CAP_TO_INDEX(CAP_SYS_BOOT)].effective |= CAP_TO_MASK(CAP_SYS_BOOT); + cap[CAP_TO_INDEX(CAP_SYS_BOOT)].permitted |= CAP_TO_MASK(CAP_SYS_BOOT); + capset(&header, cap); D("Local port disabled\n"); } else { char local_name[30]; build_local_name(local_name, sizeof(local_name), server_port); - if(install_listener(local_name, "*smartsocket*", NULL)) { + if(install_listener(local_name, "*smartsocket*", NULL, 0)) { exit(1); } } - /* for the device, start the usb transport if the - ** android usb device exists and the "service.adb.tcp.port" and - ** "persist.adb.tcp.port" properties are not set. - ** Otherwise start the network transport. - */ + int usb = 0; + if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) { + // listen on USB + usb_init(); + usb = 1; + } + + // If one of these properties is set, also listen on that port + // If one of the properties isn't set and we couldn't listen on usb, + // listen on the default port. property_get("service.adb.tcp.port", value, ""); - if (!value[0]) + if (!value[0]) { property_get("persist.adb.tcp.port", value, ""); + } if (sscanf(value, "%d", &port) == 1 && port > 0) { + printf("using port=%d\n", port); // listen on TCP port specified by service.adb.tcp.port property local_init(port); - } else if (access("/dev/android_adb", F_OK) == 0) { - // listen on USB - usb_init(); - } else { + } else if (!usb) { // listen on default port local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); } + D("adb_main(): pre init_jdwp()\n"); init_jdwp(); D("adb_main(): post init_jdwp()\n"); @@ -993,7 +1352,7 @@ void connect_device(char* host, char* buffer, int buffer_size) strncpy(hostbuf, host, sizeof(hostbuf) - 1); if (portstr) { - if (portstr - host >= sizeof(hostbuf)) { + if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) { snprintf(buffer, buffer_size, "bad host name %s", host); return; } @@ -1127,16 +1486,19 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } // return a list of all connected devices - if (!strcmp(service, "devices")) { + if (!strncmp(service, "devices", 7)) { char buffer[4096]; - memset(buf, 0, sizeof(buf)); - memset(buffer, 0, sizeof(buffer)); - D("Getting device list \n"); - list_transports(buffer, sizeof(buffer)); - snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); - D("Wrote device list \n"); - writex(reply_fd, buf, strlen(buf)); - return 0; + int use_long = !strcmp(service+7, "-l"); + if (use_long || service[7] == 0) { + memset(buf, 0, sizeof(buf)); + memset(buffer, 0, sizeof(buffer)); + D("Getting device list \n"); + list_transports(buffer, sizeof(buffer), use_long); + snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); + D("Wrote device list \n"); + writex(reply_fd, buf, strlen(buf)); + return 0; + } } // add a new TCP transport, device or emulator @@ -1202,6 +1564,16 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r writex(reply_fd, buf, strlen(buf)); return 0; } + if(!strncmp(service,"get-devpath",strlen("get-devpath"))) { + char *out = "unknown"; + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + if (transport && transport->devpath) { + out = transport->devpath; + } + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); + writex(reply_fd, buf, strlen(buf)); + return 0; + } // indicates a new emulator instance has started if (!strncmp(service,"emulator:",9)) { int port = atoi(service+9); @@ -1211,24 +1583,63 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } #endif // ADB_HOST - if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) { + if(!strcmp(service,"list-forward")) { + // Create the list of forward redirections. + char header[9]; + int buffer_size = format_listeners(NULL, 0); + // Add one byte for the trailing zero. + char* buffer = malloc(buffer_size+1); + (void) format_listeners(buffer, buffer_size+1); + snprintf(header, sizeof header, "OKAY%04x", buffer_size); + writex(reply_fd, header, 8); + writex(reply_fd, buffer, buffer_size); + free(buffer); + return 0; + } + + if (!strcmp(service,"killforward-all")) { + remove_all_listeners(); + adb_write(reply_fd, "OKAYOKAY", 8); + return 0; + } + + if(!strncmp(service,"forward:",8) || + !strncmp(service,"killforward:",12)) { char *local, *remote, *err; int r; atransport *transport; int createForward = strncmp(service,"kill",4); + int no_rebind = 0; - local = service + (createForward ? 8 : 12); - remote = strchr(local,';'); - if(remote == 0) { - sendfailmsg(reply_fd, "malformed forward spec"); - return 0; + local = strchr(service, ':') + 1; + + // Handle forward:norebind:<local>... here + if (createForward && !strncmp(local, "norebind:", 9)) { + no_rebind = 1; + local = strchr(local, ':') + 1; } - *remote++ = 0; - if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ - sendfailmsg(reply_fd, "malformed forward spec"); - return 0; + remote = strchr(local,';'); + + if (createForward) { + // Check forward: parameter format: '<local>;<remote>' + if(remote == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + + *remote++ = 0; + if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + } else { + // Check killforward: parameter format: '<local>' + if (local[0] == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } } transport = acquire_one_transport(CS_ANY, ttype, serial, &err); @@ -1238,9 +1649,9 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } if (createForward) { - r = install_listener(local, remote, transport); + r = install_listener(local, remote, transport, no_rebind); } else { - r = remove_listener(local, remote, transport); + r = remove_listener(local, transport); } if(r == 0) { /* 1st OKAY is connect, 2nd OKAY is status */ @@ -1249,7 +1660,18 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } if (createForward) { - sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket"); + const char* message; + switch (r) { + case INSTALL_STATUS_CANNOT_BIND: + message = "cannot bind to socket"; + break; + case INSTALL_STATUS_CANNOT_REBIND: + message = "cannot rebind existing socket"; + break; + default: + message = "internal error"; + } + sendfailmsg(reply_fd, message); } else { sendfailmsg(reply_fd, "cannot remove listener"); } @@ -1278,6 +1700,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; @@ -29,13 +29,14 @@ #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 #define A_WRTE 0x45545257 +#define A_AUTH 0x48545541 #define A_VERSION 0x01000000 // ADB protocol version #define ADB_VERSION_MAJOR 1 // Used for help/version information #define ADB_VERSION_MINOR 0 // Used for help/version information -#define ADB_SERVER_VERSION 29 // Increment this when we want to force users to start a new adb server +#define ADB_SERVER_VERSION 31 // Increment this when we want to force users to start a new adb server typedef struct amessage amessage; typedef struct apacket apacket; @@ -86,6 +87,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 */ @@ -160,6 +166,8 @@ typedef enum transport_type { kTransportHost, } transport_type; +#define TOKEN_SIZE 20 + struct atransport { atransport *next; @@ -176,6 +184,7 @@ struct atransport int ref_count; unsigned sync_token; int connection_state; + int online; transport_type type; /* usb handle or socket fd as needed */ @@ -185,11 +194,19 @@ struct atransport /* used to identify transports for clients */ char *serial; char *product; + char *model; + char *device; + char *devpath; int adb_port; // Use for emulators (local transport) /* a list of adisconnect callbacks called when the transport is kicked */ int kicked; adisconnect disconnects; + + void *key; + unsigned char token[TOKEN_SIZE]; + fdevent auth_fde; + unsigned failed_auth_attempts; }; @@ -248,7 +265,7 @@ int adb_main(int is_daemon, int server_port); ** get_device_transport does an acquire on your behalf before returning */ void init_transport_registration(void); -int list_transports(char *buf, size_t bufsize); +int list_transports(char *buf, size_t bufsize, int long_listing); void update_transports(void); asocket* create_device_tracker(void); @@ -281,7 +298,7 @@ void register_socket_transport(int s, const char *serial, int port, int local); void unregister_transport(atransport *t); void unregister_all_tcp_transports(); -void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable); +void register_usb_transport(usb_handle *h, const char *serial, const char *devpath, unsigned writeable); /* this should only be used for transports with connection_state == CS_NOPERM */ void unregister_usb_transport(usb_handle *usb); @@ -341,10 +358,26 @@ typedef enum { TRACE_SYSDEPS, TRACE_JDWP, /* 0x100 */ TRACE_SERVICES, + TRACE_AUTH, } AdbTrace; #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); @@ -385,7 +418,7 @@ typedef enum { #endif -#if !TRACE_PACKETS +#if !DEBUG_PACKETS #define print_packet(tag,p) do {} while (0) #endif @@ -434,12 +467,24 @@ 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; #define CHUNK_SIZE (64*1024) +#if !ADB_HOST +#define USB_ADB_PATH "/dev/android_adb" + +#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/" +#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x + +#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0) +#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1) +#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2) +#endif + int sendfailmsg(int fd, const char *reason); int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); diff --git a/adb/adb_auth.h b/adb/adb_auth.h new file mode 100644 index 0000000..1fffa49 --- /dev/null +++ b/adb/adb_auth.h @@ -0,0 +1,54 @@ +/* + * 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 __ADB_AUTH_H +#define __ADB_AUTH_H + +void adb_auth_init(void); +void adb_auth_verified(atransport *t); + +/* AUTH packets first argument */ +/* Request */ +#define ADB_AUTH_TOKEN 1 +/* Response */ +#define ADB_AUTH_SIGNATURE 2 +#define ADB_AUTH_RSAPUBLICKEY 3 + +#if ADB_HOST + +int adb_auth_sign(void *key, void *token, size_t token_size, void *sig); +void *adb_auth_nextkey(void *current); +int adb_auth_get_userkey(unsigned char *data, size_t len); + +static inline int adb_auth_generate_token(void *token, size_t token_size) { return 0; } +static inline int adb_auth_verify(void *token, void *sig, int siglen) { return 0; } +static inline void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t) { } +static inline void adb_auth_reload_keys(void) { } + +#else // !ADB_HOST + +static inline int adb_auth_sign(void* key, void *token, size_t token_size, void *sig) { return 0; } +static inline void *adb_auth_nextkey(void *current) { return NULL; } +static inline int adb_auth_get_userkey(unsigned char *data, size_t len) { return 0; } + +int adb_auth_generate_token(void *token, size_t token_size); +int adb_auth_verify(void *token, void *sig, int siglen); +void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t); +void adb_auth_reload_keys(void); + +#endif // ADB_HOST + +#endif // __ADB_AUTH_H diff --git a/adb/adb_auth_client.c b/adb/adb_auth_client.c new file mode 100644 index 0000000..0b4913e --- /dev/null +++ b/adb/adb_auth_client.c @@ -0,0 +1,245 @@ +/* + * 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 <resolv.h> +#include <cutils/list.h> +#include <cutils/sockets.h> + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" +#include "fdevent.h" +#include "mincrypt/rsa.h" + +#define TRACE_TAG TRACE_AUTH + + +struct adb_public_key { + struct listnode node; + RSAPublicKey key; +}; + +static struct listnode key_list; + +static char *key_paths[] = { + "/adb_keys", + "/data/misc/adb/adb_keys", + NULL +}; + +static fdevent listener_fde; +static int framework_fd = -1; + + +static void read_keys(const char *file, struct listnode *list) +{ + struct adb_public_key *key; + FILE *f; + char buf[MAX_PAYLOAD]; + char *sep; + int ret; + + f = fopen(file, "r"); + if (!f) { + D("Can't open '%s'\n", file); + return; + } + + while (fgets(buf, sizeof(buf), f)) { + /* Allocate 4 extra bytes to decode the base64 data in-place */ + key = calloc(1, sizeof(*key) + 4); + if (!key) { + D("Can't malloc key\n"); + break; + } + + sep = strpbrk(buf, " \t"); + if (sep) + *sep = '\0'; + + ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4); + if (ret != sizeof(key->key)) { + D("%s: Invalid base64 data ret=%d\n", file, ret); + free(key); + continue; + } + + if (key->key.len != RSANUMWORDS) { + D("%s: Invalid key len %d\n", file, key->key.len); + free(key); + continue; + } + + list_add_tail(list, &key->node); + } + + fclose(f); +} + +static void free_keys(struct listnode *list) +{ + struct listnode *item; + + while (!list_empty(list)) { + item = list_head(list); + list_remove(item); + free(node_to_item(item, struct adb_public_key, node)); + } +} + +void adb_auth_reload_keys(void) +{ + char *path; + char **paths = key_paths; + struct stat buf; + + free_keys(&key_list); + + while ((path = *paths++)) { + if (!stat(path, &buf)) { + D("Loading keys from '%s'\n", path); + read_keys(path, &key_list); + } + } +} + +int adb_auth_generate_token(void *token, size_t token_size) +{ + FILE *f; + int ret; + + f = fopen("/dev/urandom", "r"); + if (!f) + return 0; + + ret = fread(token, token_size, 1, f); + + fclose(f); + return ret * token_size; +} + +int adb_auth_verify(void *token, void *sig, int siglen) +{ + struct listnode *item; + struct adb_public_key *key; + int ret; + + if (siglen != RSANUMBYTES) + return 0; + + list_for_each(item, &key_list) { + key = node_to_item(item, struct adb_public_key, node); + ret = RSA_verify(&key->key, sig, siglen, token); + if (ret) + return 1; + } + + return 0; +} + +static void adb_auth_event(int fd, unsigned events, void *data) +{ + atransport *t = data; + char response[2]; + int ret; + + if (events & FDE_READ) { + ret = unix_read(fd, response, sizeof(response)); + if (ret < 0) { + D("Disconnect"); + fdevent_remove(&t->auth_fde); + framework_fd = -1; + } + else if (ret == 2 && response[0] == 'O' && response[1] == 'K') { + adb_auth_reload_keys(); + adb_auth_verified(t); + } + } +} + +void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t) +{ + char msg[MAX_PAYLOAD]; + int ret; + + if (framework_fd < 0) { + D("Client not connected\n"); + return; + } + + if (key[len - 1] != '\0') { + D("Key must be a null-terminated string\n"); + return; + } + + ret = snprintf(msg, sizeof(msg), "PK%s", key); + if (ret >= (signed)sizeof(msg)) { + D("Key too long. ret=%d", ret); + return; + } + D("Sending '%s'\n", msg); + + ret = unix_write(framework_fd, msg, ret); + if (ret < 0) { + D("Failed to write PK, errno=%d\n", errno); + return; + } + + fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t); + fdevent_add(&t->auth_fde, FDE_READ); +} + +static void adb_auth_listener(int fd, unsigned events, void *data) +{ + struct sockaddr addr; + socklen_t alen; + int s; + + alen = sizeof(addr); + + s = adb_socket_accept(fd, &addr, &alen); + if (s < 0) { + D("Failed to accept: errno=%d\n", errno); + return; + } + + framework_fd = s; +} + +void adb_auth_init(void) +{ + int fd, ret; + + list_init(&key_list); + adb_auth_reload_keys(); + + fd = android_get_control_socket("adbd"); + if (fd < 0) { + D("Failed to get adbd socket\n"); + return; + } + + ret = listen(fd, 4); + if (ret < 0) { + D("Failed to listen on '%d'\n", fd); + return; + } + + fdevent_install(&listener_fde, fd, adb_auth_listener, NULL); + fdevent_add(&listener_fde, FDE_READ); +} diff --git a/adb/adb_auth_host.c b/adb/adb_auth_host.c new file mode 100644 index 0000000..9039d42 --- /dev/null +++ b/adb/adb_auth_host.c @@ -0,0 +1,426 @@ +/* + * 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> + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +# include "shlobj.h" +#else +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#endif +#include <string.h> + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" + +/* HACK: we need the RSAPublicKey struct + * but RSA_verify conflits with openssl */ +#define RSA_verify RSA_verify_mincrypt +#include "mincrypt/rsa.h" +#undef RSA_verify + +#include <cutils/list.h> + +#include <openssl/evp.h> +#include <openssl/objects.h> +#include <openssl/pem.h> +#include <openssl/rsa.h> +#include <openssl/sha.h> + +#define TRACE_TAG TRACE_AUTH + +#define ANDROID_PATH ".android" +#define ADB_KEY_FILE "adbkey" + + +struct adb_private_key { + struct listnode node; + RSA *rsa; +}; + +static struct listnode key_list; + + +/* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */ +static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey) +{ + int ret = 1; + unsigned int i; + + BN_CTX* ctx = BN_CTX_new(); + BIGNUM* r32 = BN_new(); + BIGNUM* rr = BN_new(); + BIGNUM* r = BN_new(); + BIGNUM* rem = BN_new(); + BIGNUM* n = BN_new(); + BIGNUM* n0inv = BN_new(); + + if (RSA_size(rsa) != RSANUMBYTES) { + ret = 0; + goto out; + } + + BN_set_bit(r32, 32); + BN_copy(n, rsa->n); + BN_set_bit(r, RSANUMWORDS * 32); + BN_mod_sqr(rr, r, n, ctx); + BN_div(NULL, rem, n, r32, ctx); + BN_mod_inverse(n0inv, rem, r32, ctx); + + pkey->len = RSANUMWORDS; + pkey->n0inv = 0 - BN_get_word(n0inv); + for (i = 0; i < RSANUMWORDS; i++) { + BN_div(rr, rem, rr, r32, ctx); + pkey->rr[i] = BN_get_word(rem); + BN_div(n, rem, n, r32, ctx); + pkey->n[i] = BN_get_word(rem); + } + pkey->exponent = BN_get_word(rsa->e); + +out: + BN_free(n0inv); + BN_free(n); + BN_free(rem); + BN_free(r); + BN_free(rr); + BN_free(r32); + BN_CTX_free(ctx); + + return ret; +} + +static void get_user_info(char *buf, size_t len) +{ + char hostname[1024], username[1024]; + int ret; + +#ifndef _WIN32 + ret = gethostname(hostname, sizeof(hostname)); + if (ret < 0) +#endif + strcpy(hostname, "unknown"); + +#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET + ret = getlogin_r(username, sizeof(username)); + if (ret < 0) +#endif + strcpy(username, "unknown"); + + ret = snprintf(buf, len, " %s@%s", username, hostname); + if (ret >= (signed)len) + buf[len - 1] = '\0'; +} + +static int write_public_keyfile(RSA *private_key, const char *private_key_path) +{ + RSAPublicKey pkey; + BIO *bio, *b64, *bfile; + char path[PATH_MAX], info[MAX_PAYLOAD]; + int ret; + + ret = snprintf(path, sizeof(path), "%s.pub", private_key_path); + if (ret >= (signed)sizeof(path)) + return 0; + + ret = RSA_to_RSAPublicKey(private_key, &pkey); + if (!ret) { + D("Failed to convert to publickey\n"); + return 0; + } + + bfile = BIO_new_file(path, "w"); + if (!bfile) { + D("Failed to open '%s'\n", path); + return 0; + } + + D("Writing public key to '%s'\n", path); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + bio = BIO_push(b64, bfile); + BIO_write(bio, &pkey, sizeof(pkey)); + BIO_flush(bio); + BIO_pop(b64); + BIO_free(b64); + + get_user_info(info, sizeof(info)); + BIO_write(bfile, info, strlen(info)); + BIO_flush(bfile); + BIO_free_all(bfile); + + return 1; +} + +static int generate_key(const char *file) +{ + EVP_PKEY* pkey = EVP_PKEY_new(); + BIGNUM* exponent = BN_new(); + RSA* rsa = RSA_new(); + mode_t old_mask; + FILE *f = NULL; + int ret = 0; + + D("generate_key '%s'\n", file); + + if (!pkey || !exponent || !rsa) { + D("Failed to allocate key\n"); + goto out; + } + + BN_set_word(exponent, RSA_F4); + RSA_generate_key_ex(rsa, 2048, exponent, NULL); + EVP_PKEY_set1_RSA(pkey, rsa); + + old_mask = umask(077); + + f = fopen(file, "w"); + if (!f) { + D("Failed to open '%s'\n", file); + umask(old_mask); + goto out; + } + + umask(old_mask); + + if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) { + D("Failed to write key\n"); + goto out; + } + + if (!write_public_keyfile(rsa, file)) { + D("Failed to write public key\n"); + goto out; + } + + ret = 1; + +out: + if (f) + fclose(f); + EVP_PKEY_free(pkey); + RSA_free(rsa); + BN_free(exponent); + return ret; +} + +static int read_key(const char *file, struct listnode *list) +{ + struct adb_private_key *key; + FILE *f; + + D("read_key '%s'\n", file); + + f = fopen(file, "r"); + if (!f) { + D("Failed to open '%s'\n", file); + return 0; + } + + key = malloc(sizeof(*key)); + if (!key) { + D("Failed to alloc key\n"); + fclose(f); + return 0; + } + key->rsa = RSA_new(); + + if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) { + D("Failed to read key\n"); + fclose(f); + RSA_free(key->rsa); + free(key); + return 0; + } + + fclose(f); + list_add_tail(list, &key->node); + return 1; +} + +static int get_user_keyfilepath(char *filename, size_t len) +{ + const char *format, *home; + char android_dir[PATH_MAX]; + struct stat buf; +#ifdef _WIN32 + char path[PATH_MAX]; + home = getenv("ANDROID_SDK_HOME"); + if (!home) { + SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path); + home = path; + } + format = "%s\\%s"; +#else + home = getenv("HOME"); + if (!home) + return -1; + format = "%s/%s"; +#endif + + D("home '%s'\n", home); + + if (snprintf(android_dir, sizeof(android_dir), format, home, + ANDROID_PATH) >= (int)sizeof(android_dir)) + return -1; + + if (stat(android_dir, &buf)) { + if (adb_mkdir(android_dir, 0750) < 0) { + D("Cannot mkdir '%s'", android_dir); + return -1; + } + } + + return snprintf(filename, len, format, android_dir, ADB_KEY_FILE); +} + +static int get_user_key(struct listnode *list) +{ + struct stat buf; + char path[PATH_MAX]; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path)); + if (ret < 0 || ret >= (signed)sizeof(path)) { + D("Error getting user key filename"); + return 0; + } + + D("user key '%s'\n", path); + + if (stat(path, &buf) == -1) { + if (!generate_key(path)) { + D("Failed to generate new key\n"); + return 0; + } + } + + return read_key(path, list); +} + +static void get_vendor_keys(struct listnode *list) +{ + const char *adb_keys_path; + char keys_path[MAX_PAYLOAD]; + char *path; + char *save; + struct stat buf; + + adb_keys_path = getenv("ADB_VENDOR_KEYS"); + if (!adb_keys_path) + return; + strncpy(keys_path, adb_keys_path, sizeof(keys_path)); + + path = adb_strtok_r(keys_path, ENV_PATH_SEPARATOR_STR, &save); + while (path) { + D("Reading: '%s'\n", path); + + if (stat(path, &buf)) + D("Can't read '%s'\n", path); + else if (!read_key(path, list)) + D("Failed to read '%s'\n", path); + + path = adb_strtok_r(NULL, ENV_PATH_SEPARATOR_STR, &save); + } +} + +int adb_auth_sign(void *node, void *token, size_t token_size, void *sig) +{ + unsigned int len; + struct adb_private_key *key = node_to_item(node, struct adb_private_key, node); + + if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) { + return 0; + } + + D("adb_auth_sign len=%d\n", len); + return (int)len; +} + +void *adb_auth_nextkey(void *current) +{ + struct listnode *item; + + if (list_empty(&key_list)) + return NULL; + + if (!current) + return list_head(&key_list); + + list_for_each(item, &key_list) { + if (item == current) { + /* current is the last item, we tried all the keys */ + if (item->next == &key_list) + return NULL; + return item->next; + } + } + + return NULL; +} + +int adb_auth_get_userkey(unsigned char *data, size_t len) +{ + char path[PATH_MAX]; + char *file; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path) - 4); + if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) { + D("Error getting user key filename"); + return 0; + } + strcat(path, ".pub"); + + file = load_file(path, (unsigned*)&ret); + if (!file) { + D("Can't load '%s'\n", path); + return 0; + } + + if (len < (size_t)(ret + 1)) { + D("%s: Content too large ret=%d\n", path, ret); + return 0; + } + + memcpy(data, file, ret); + data[ret] = '\0'; + + return ret + 1; +} + +void adb_auth_init(void) +{ + int ret; + + D("adb_auth_init\n"); + + list_init(&key_list); + + ret = get_user_key(&key_list); + if (!ret) { + D("Failed to get user key\n"); + return; + } + + get_vendor_keys(&key_list); +} diff --git a/adb/adb_client.c b/adb/adb_client.c index 9a812f0..8340738 100644 --- a/adb/adb_client.c +++ b/adb/adb_client.c @@ -17,6 +17,7 @@ static transport_type __adb_transport = kTransportAny; static const char* __adb_serial = NULL; static int __adb_server_port = DEFAULT_ADB_PORT; +static const char* __adb_server_name = NULL; void adb_set_transport(transport_type type, const char* serial) { @@ -29,6 +30,11 @@ void adb_set_tcp_specifics(int server_port) __adb_server_port = server_port; } +void adb_set_tcp_name(const char* hostname) +{ + __adb_server_name = hostname; +} + int adb_get_emulator_console_port(void) { const char* serial = __adb_serial; @@ -181,7 +187,11 @@ int _adb_connect(const char *service) } snprintf(tmp, sizeof tmp, "%04x", len); - fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); + if (__adb_server_name) + fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM); + else + fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); + if(fd < 0) { strcpy(__adb_error, "cannot connect to daemon"); return -2; @@ -212,7 +222,10 @@ int adb_connect(const char *service) int fd = _adb_connect("host:version"); D("adb_connect: service %s\n", service); - if(fd == -2) { + if(fd == -2 && __adb_server_name) { + fprintf(stderr,"** Cannot start server on remote host\n"); + return fd; + } else if(fd == -2) { fprintf(stdout,"* daemon not running. starting it now on port %d *\n", __adb_server_port); start_server: @@ -266,7 +279,7 @@ int adb_connect(const char *service) fd = _adb_connect(service); if(fd == -2) { - fprintf(stderr,"** daemon still not running"); + fprintf(stderr,"** daemon still not running\n"); } D("adb_connect: return fd %d\n", fd); diff --git a/adb/adb_client.h b/adb/adb_client.h index 40ab189..0ec47ca 100644 --- a/adb/adb_client.h +++ b/adb/adb_client.h @@ -29,6 +29,10 @@ void adb_set_transport(transport_type type, const char* serial); */ void adb_set_tcp_specifics(int server_port); +/* Set TCP Hostname of the transport to use +*/ +void adb_set_tcp_name(const char* hostname); + /* Return the console port of the currently connected emulator (if any) * of -1 if there is no emulator, and -2 if there is more than one. * assumes adb_set_transport() was alled previously... diff --git a/adb/commandline.c b/adb/commandline.c index ffc120f..a927423 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -46,6 +46,7 @@ int install_app(transport_type transport, char* serial, int argc, char** argv); int uninstall_app(transport_type transport, char* serial, int argc, char** argv); static const char *gProductOutPath = NULL; +extern int gListenAll; static char *product_file(const char *extra) { @@ -80,12 +81,13 @@ void help() fprintf(stderr, "\n" + " -a - directs adb to listen on all interfaces for a connection\n" " -d - directs command to the only connected USB device\n" " returns an error if more than one USB device is present.\n" " -e - directs command to the only running emulator.\n" " returns an error if more than one emulator is running.\n" - " -s <serial number> - directs command to the USB device or emulator with\n" - " the given serial number. Overrides ANDROID_SERIAL\n" + " -s <specific device> - directs command to the device or emulator with the given\n" + " serial number or qualifier. Overrides ANDROID_SERIAL\n" " environment variable.\n" " -p <product name or path> - simple product name like 'sooner', or\n" " a relative/absolute path to a product\n" @@ -93,7 +95,10 @@ void help() " If -p is not specified, the ANDROID_PRODUCT_OUT\n" " environment variable is used, which must\n" " be an absolute path.\n" - " devices - list all connected devices\n" + " -H - Name of adb server host (default: localhost)\n" + " -P - Port of adb server (default: 5037)\n" + " devices [-l] - list all connected devices\n" + " ('-l' will also list device qualifiers)\n" " connect <host>[:<port>] - connect to a device via TCP/IP\n" " Port 5555 is used by default if no port number is specified.\n" " disconnect [<host>[:<port>]] - disconnect from a TCP/IP device.\n" @@ -111,6 +116,9 @@ void help() " adb shell <command> - run remote shell command\n" " adb emu <command> - run emulator console command\n" " adb logcat [ <filter-spec> ] - View device log\n" + " adb forward --list - list all forward socket connections.\n" + " the format is a list of lines with the following format:\n" + " <serial> \" \" <local> \" \" <remote> \"\\n\"\n" " adb forward <local> <remote> - forward socket connections\n" " forward specs are one of: \n" " tcp:<port>\n" @@ -119,11 +127,18 @@ void help() " localfilesystem:<unix domain socket name>\n" " dev:<character device name>\n" " jdwp:<process pid> (remote only)\n" + " adb forward --no-rebind <local> <remote>\n" + " - same as 'adb forward <local> <remote>' but fails\n" + " if <local> is already forwarded\n" + " adb forward --remove <local> - remove a specific forward socket connection\n" + " adb forward --remove-all - remove all forward socket connections\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" @@ -157,6 +172,7 @@ void help() " adb kill-server - kill the server if it is running\n" " adb get-state - prints: offline | bootloader | device\n" " adb get-serialno - prints: <serial-number>\n" + " adb get-devpath - prints: <device-path>\n" " adb status-window - continuously print device status for a specified device\n" " adb remount - remounts the /system partition on the device read-write\n" " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n" @@ -367,6 +383,83 @@ static void format_host_command(char* buffer, size_t buflen, const char* comman } } +int adb_download_buffer(const char *service, const char *fn, 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", fn, (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", fn); + return -1; + } + + int status = adb_download_buffer(service, fn, data, sz, progress); + free(data); + return status; +} + static void status_window(transport_type ttype, const char* serial) { char command[4096]; @@ -561,6 +654,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 +741,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 +769,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); @@ -851,9 +950,9 @@ int adb_commandline(int argc, char **argv) int server_port = DEFAULT_ADB_PORT; if (server_port_str && strlen(server_port_str) > 0) { server_port = (int) strtol(server_port_str, NULL, 0); - if (server_port <= 0) { + if (server_port <= 0 || server_port > 65535) { fprintf(stderr, - "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number. Got \"%s\"\n", + "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65535. Got \"%s\"\n", server_port_str); return usage(); } @@ -878,7 +977,7 @@ int adb_commandline(int argc, char **argv) argc--; argv++; } else { - product = argv[1] + 2; + product = argv[0] + 2; } gProductOutPath = find_product_out_path(product); if (gProductOutPath == NULL) { @@ -899,6 +998,42 @@ int adb_commandline(int argc, char **argv) ttype = kTransportUsb; } else if (!strcmp(argv[0],"-e")) { ttype = kTransportLocal; + } else if (!strcmp(argv[0],"-a")) { + gListenAll = 1; + } else if(!strncmp(argv[0], "-H", 2)) { + const char *hostname = NULL; + if (argv[0][2] == '\0') { + if (argc < 2) return usage(); + hostname = argv[1]; + argc--; + argv++; + } else { + hostname = argv[0] + 2; + } + adb_set_tcp_name(hostname); + + } else if(!strncmp(argv[0], "-P", 2)) { + if (argv[0][2] == '\0') { + if (argc < 2) return usage(); + server_port_str = argv[1]; + argc--; + argv++; + } else { + server_port_str = argv[0] + 2; + } + if (strlen(server_port_str) > 0) { + server_port = (int) strtol(server_port_str, NULL, 0); + if (server_port <= 0 || server_port > 65535) { + fprintf(stderr, + "adb: port number must be a positive number less than 65536. Got \"%s\"\n", + server_port_str); + return usage(); + } + } else { + fprintf(stderr, + "adb: port number must be a positive number less than 65536. Got empty string.\n"); + return usage(); + } } else { /* out of recognized modifiers and flags */ break; @@ -931,7 +1066,16 @@ top: if(!strcmp(argv[0], "devices")) { char *tmp; - snprintf(buf, sizeof buf, "host:%s", argv[0]); + char *listopt; + if (argc < 2) + listopt = ""; + else if (argc == 2 && !strcmp(argv[1], "-l")) + listopt = argv[1]; + else { + fprintf(stderr, "Usage: adb devices [-l]\n"); + return 1; + } + snprintf(buf, sizeof buf, "host:%s%s", argv[0], listopt); tmp = adb_query(buf); if(tmp) { printf("List of devices attached \n"); @@ -1057,6 +1201,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") @@ -1118,16 +1271,85 @@ top: } if(!strcmp(argv[0], "forward")) { - if(argc != 3) return usage(); + char host_prefix[64]; + char remove = 0; + char remove_all = 0; + char list = 0; + char no_rebind = 0; + + // Parse options here. + while (argc > 1 && argv[1][0] == '-') { + if (!strcmp(argv[1], "--list")) + list = 1; + else if (!strcmp(argv[1], "--remove")) + remove = 1; + else if (!strcmp(argv[1], "--remove-all")) + remove_all = 1; + else if (!strcmp(argv[1], "--no-rebind")) + no_rebind = 1; + else { + return usage(); + } + argc--; + argv++; + } + + // Ensure we can only use one option at a time. + if (list + remove + remove_all + no_rebind > 1) { + return usage(); + } + + // Determine the <host-prefix> for this command. if (serial) { - snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial, argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host-serial:%s", + serial); } else if (ttype == kTransportUsb) { - snprintf(buf, sizeof buf, "host-usb:forward:%s;%s", argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host-usb"); } else if (ttype == kTransportLocal) { - snprintf(buf, sizeof buf, "host-local:forward:%s;%s", argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host-local"); } else { - snprintf(buf, sizeof buf, "host:forward:%s;%s", argv[1], argv[2]); + snprintf(host_prefix, sizeof host_prefix, "host"); + } + + // Implement forward --list + if (list) { + if (argc != 1) + return usage(); + snprintf(buf, sizeof buf, "%s:list-forward", host_prefix); + char* forwards = adb_query(buf); + if (forwards == NULL) { + fprintf(stderr, "error: %s\n", adb_error()); + return 1; + } + printf("%s", forwards); + free(forwards); + return 0; + } + + // Implement forward --remove-all + else if (remove_all) { + if (argc != 1) + return usage(); + snprintf(buf, sizeof buf, "%s:killforward-all", host_prefix); } + + // Implement forward --remove <local> + else if (remove) { + if (argc != 2) + return usage(); + snprintf(buf, sizeof buf, "%s:killforward:%s", host_prefix, argv[1]); + } + // Or implement one of: + // forward <local> <remote> + // forward --no-rebind <local> <remote> + else + { + if (argc != 3) + return usage(); + const char* command = no_rebind ? "forward:norebind:" : "forward"; + snprintf(buf, sizeof buf, "%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]); + } + if(adb_command(buf)) { fprintf(stderr,"error: %s\n", adb_error()); return 1; @@ -1204,7 +1426,8 @@ top: /* passthrough commands */ if(!strcmp(argv[0],"get-state") || - !strcmp(argv[0],"get-serialno")) + !strcmp(argv[0],"get-serialno") || + !strcmp(argv[0],"get-devpath")) { char *tmp; @@ -1225,7 +1448,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 +1663,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 +1674,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 +1714,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 +1732,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/file_sync_client.c b/adb/file_sync_client.c index 64e393c..354d0fb 100644 --- a/adb/file_sync_client.c +++ b/adb/file_sync_client.c @@ -32,7 +32,7 @@ #include "file_sync_service.h" -static unsigned total_bytes; +static unsigned long long total_bytes; static long long start_time; static long long NOW() @@ -58,8 +58,8 @@ static void END() t = 1000000; fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n", - ((((long long) total_bytes) * 1000000LL) / t) / 1024LL, - (long long) total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL); + ((total_bytes * 1000000LL) / t) / 1024LL, + total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL); } void sync_quit(int fd) 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/protocol.txt b/adb/protocol.txt index 398d042..c9d3c24 100644 --- a/adb/protocol.txt +++ b/adb/protocol.txt @@ -72,7 +72,25 @@ large maxdata value, the connection with the other side must be closed. The system identity string should be "<systemtype>:<serialno>:<banner>" where systemtype is "bootloader", "device", or "host", serialno is some kind of unique ID (or empty), and banner is a human-readable version -or identifier string (informational only). +or identifier string. The banner is used to transmit useful properties. + + +--- AUTH(type, 0, "data") ---------------------------------------------- + +The AUTH message informs the recipient that authentication is required to +connect to the sender. If type is TOKEN(1), data is a random token that +the recipient can sign with a private key. The recipient replies with an +AUTH packet where type is SIGNATURE(2) and data is the signature. If the +signature verification succeeds, the sender replies with a CONNECT packet. + +If the signature verification fails, the sender replies with a new AUTH +packet and a new random token, so that the recipient can retry signing +with a different private key. + +Once the recipient has tried all its private keys, it can reply with an +AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If +possible, an on-screen confirmation may be displayed for the user to +confirm they want to install the public key on the device. --- OPEN(local-id, 0, "destination") ----------------------------------- @@ -166,6 +184,7 @@ to send across the wire. #define A_SYNC 0x434e5953 #define A_CNXN 0x4e584e43 +#define A_AUTH 0x48545541 #define A_OPEN 0x4e45504f #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 diff --git a/adb/services.c b/adb/services.c index 6940be8..54d21a8 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) @@ -212,7 +202,7 @@ static void echo_service(int fd, void *cookie) int c; for(;;) { - r = read(fd, buf, 4096); + r = adb_read(fd, buf, 4096); if(r == 0) goto done; if(r < 0) { if(errno == EINTR) continue; @@ -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..305cb44 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; } @@ -591,12 +608,30 @@ unsigned unhex(unsigned char *s, int len) return n; } +#define PREFIX(str) { str, sizeof(str) - 1 } +static const struct prefix_struct { + const char *str; + const size_t len; +} prefixes[] = { + PREFIX("usb:"), + PREFIX("product:"), + PREFIX("model:"), + PREFIX("device:"), +}; +static const int num_prefixes = (sizeof(prefixes) / sizeof(prefixes[0])); + /* skip_host_serial return the position in a string skipping over the 'serial' parameter in the ADB protocol, where parameter string may be a host:port string containing the protocol delimiter (colon). */ char *skip_host_serial(char *service) { char *first_colon, *serial_end; + int i; + + for (i = 0; i < num_prefixes; i++) { + if (!strncmp(service, prefixes[i].str, prefixes[i].len)) + return strchr(service + prefixes[i].len, ':'); + } first_colon = strchr(service, ':'); if (!first_colon) { diff --git a/adb/sysdeps.h b/adb/sysdeps.h index b518076..0252ef3 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -38,6 +38,7 @@ #define OS_PATH_SEPARATOR '\\' #define OS_PATH_SEPARATOR_STR "\\" +#define ENV_PATH_SEPARATOR_STR ";" typedef CRITICAL_SECTION adb_mutex_t; @@ -254,6 +255,8 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; } +extern char* adb_strtok_r(char *str, const char *delim, char **saveptr); + #else /* !_WIN32 a.k.a. Unix */ #include "fdevent.h" @@ -272,9 +275,26 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) #include <netinet/in.h> #include <netinet/tcp.h> #include <string.h> +#include <unistd.h> + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif #define OS_PATH_SEPARATOR '/' #define OS_PATH_SEPARATOR_STR "/" +#define ENV_PATH_SEPARATOR_STR ":" typedef pthread_mutex_t adb_mutex_t; @@ -306,7 +326,7 @@ static __inline__ int unix_open(const char* path, int options,...) { if ((options & O_CREAT) == 0) { - return open(path, options); + return TEMP_FAILURE_RETRY( open(path, options) ); } else { @@ -315,19 +335,19 @@ static __inline__ int unix_open(const char* path, int options,...) va_start( args, options ); mode = va_arg( args, int ); va_end( args ); - return open(path, options, mode); + return TEMP_FAILURE_RETRY( open( path, options, mode ) ); } } static __inline__ int adb_open_mode( const char* pathname, int options, int mode ) { - return open( pathname, options, mode ); + return TEMP_FAILURE_RETRY( open( pathname, options, mode ) ); } static __inline__ int adb_open( const char* pathname, int options ) { - int fd = open( pathname, options ); + int fd = TEMP_FAILURE_RETRY( open( pathname, options ) ); if (fd < 0) return -1; close_on_exec( fd ); @@ -353,7 +373,7 @@ static __inline__ int adb_close(int fd) static __inline__ int adb_read(int fd, void* buf, size_t len) { - return read(fd, buf, len); + return TEMP_FAILURE_RETRY( read( fd, buf, len ) ); } #undef read @@ -361,7 +381,7 @@ static __inline__ int adb_read(int fd, void* buf, size_t len) static __inline__ int adb_write(int fd, const void* buf, size_t len) { - return write(fd, buf, len); + return TEMP_FAILURE_RETRY( write( fd, buf, len ) ); } #undef write #define write ___xxx_write @@ -382,7 +402,7 @@ static __inline__ int adb_unlink(const char* path) static __inline__ int adb_creat(const char* path, int mode) { - int fd = creat(path, mode); + int fd = TEMP_FAILURE_RETRY( creat( path, mode ) ); if ( fd < 0 ) return -1; @@ -397,7 +417,7 @@ static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, { int fd; - fd = accept(serverfd, addr, addrlen); + fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) ); if (fd >= 0) close_on_exec(fd); @@ -490,6 +510,13 @@ static __inline__ int adb_is_absolute_host_path( const char* path ) return path[0] == '/'; } +static __inline__ char* adb_strtok_r(char *str, const char *delim, char **saveptr) +{ + return strtok_r(str, delim, saveptr); +} +#undef strtok_r +#define strtok_r ___xxx_strtok_r + #endif /* !_WIN32 */ #endif /* _ADB_SYSDEPS_H */ diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c index c426718..2105b16 100644 --- a/adb/sysdeps_win32.c +++ b/adb/sysdeps_win32.c @@ -781,7 +781,7 @@ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrle void disable_tcp_nagle(int fd) { FH fh = _fh_from_int(fd); - int on; + int on = 1; if ( !fh || fh->clazz != &_fh_socket_class ) return; @@ -2140,3 +2140,81 @@ adb_sysdeps_init( void ) InitializeCriticalSection( &_win32_lock ); } +/* Windows doesn't have strtok_r. Use the one from bionic. */ + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +char * +adb_strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + if (s == NULL && (s = *last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} diff --git a/adb/transport.c b/adb/transport.c index 83a349a..9fd6cc2 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -370,7 +370,7 @@ static int list_transports_msg(char* buffer, size_t bufferlen) char head[5]; int len; - len = list_transports(buffer+4, bufferlen-4); + len = list_transports(buffer+4, bufferlen-4, 0); snprintf(head, sizeof(head), "%04x", len); memcpy(buffer, head, 4); len += 4; @@ -601,6 +601,12 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) free(t->product); if (t->serial) free(t->serial); + if (t->model) + free(t->model); + if (t->device) + free(t->device); + if (t->devpath) + free(t->devpath); memset(t,0xee,sizeof(atransport)); free(t); @@ -737,6 +743,45 @@ void remove_transport_disconnect(atransport* t, adisconnect* dis) dis->next = dis->prev = dis; } +static int qual_char_is_invalid(char ch) +{ + if ('A' <= ch && ch <= 'Z') + return 0; + if ('a' <= ch && ch <= 'z') + return 0; + if ('0' <= ch && ch <= '9') + return 0; + return 1; +} + +static int qual_match(const char *to_test, + const char *prefix, const char *qual, int sanitize_qual) +{ + if (!to_test || !*to_test) + /* Return true if both the qual and to_test are null strings. */ + return !qual || !*qual; + + if (!qual) + return 0; + + if (prefix) { + while (*prefix) { + if (*prefix++ != *to_test++) + return 0; + } + } + + while (*qual) { + char ch = *qual++; + if (sanitize_qual && qual_char_is_invalid(ch)) + ch = '_'; + if (ch != *to_test++) + return 0; + } + + /* Everything matched so far. Return true if *to_test is a NUL. */ + return !*to_test; +} atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out) { @@ -758,9 +803,19 @@ retry: /* check for matching serial number */ if (serial) { - if (t->serial && !strcmp(serial, t->serial)) { + if ((t->serial && !strcmp(serial, t->serial)) || + (t->devpath && !strcmp(serial, t->devpath)) || + qual_match(serial, "product:", t->product, 0) || + qual_match(serial, "model:", t->model, 1) || + qual_match(serial, "device:", t->device, 0)) { + if (result) { + if (error_out) + *error_out = "more than one device"; + ambiguous = 1; + result = NULL; + break; + } result = t; - break; } } else { if (ttype == kTransportUsb && t->type == kTransportUsb) { @@ -831,12 +886,64 @@ 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"; } } -int list_transports(char *buf, size_t bufsize) +static void add_qual(char **buf, size_t *buf_size, + const char *prefix, const char *qual, int sanitize_qual) +{ + size_t len; + int prefix_len; + + if (!buf || !*buf || !buf_size || !*buf_size || !qual || !*qual) + return; + + len = snprintf(*buf, *buf_size, "%s%n%s", prefix, &prefix_len, qual); + + if (sanitize_qual) { + char *cp; + for (cp = *buf + prefix_len; cp < *buf + len; cp++) { + if (qual_char_is_invalid(*cp)) + *cp = '_'; + } + } + + *buf_size -= len; + *buf += len; +} + +static size_t format_transport(atransport *t, char *buf, size_t bufsize, + int long_listing) +{ + const char* serial = t->serial; + if (!serial || !serial[0]) + serial = "????????????"; + + if (!long_listing) { + return snprintf(buf, bufsize, "%s\t%s\n", serial, statename(t)); + } else { + size_t len, remaining = bufsize; + + len = snprintf(buf, remaining, "%-22s %s", serial, statename(t)); + remaining -= len; + buf += len; + + add_qual(&buf, &remaining, " ", t->devpath, 0); + add_qual(&buf, &remaining, " product:", t->product, 0); + add_qual(&buf, &remaining, " model:", t->model, 1); + add_qual(&buf, &remaining, " device:", t->device, 0); + + len = snprintf(buf, remaining, "\n"); + remaining -= len; + + return bufsize - remaining; + } +} + +int list_transports(char *buf, size_t bufsize, int long_listing) { char* p = buf; char* end = buf + bufsize; @@ -846,11 +953,7 @@ int list_transports(char *buf, size_t bufsize) /* XXX OVERRUN PROBLEMS XXX */ adb_mutex_lock(&transport_lock); for(t = transport_list.next; t != &transport_list; t = t->next) { - const char* serial = t->serial; - if (!serial || !serial[0]) - serial = "????????????"; - len = snprintf(p, end - p, "%s\t%s\n", serial, statename(t)); - + len = format_transport(t, p, end - p, long_listing); if (p + len >= end) { /* discard last line if buffer is too short */ break; @@ -955,7 +1058,7 @@ void unregister_all_tcp_transports() #endif -void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable) +void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable) { atransport *t = calloc(1, sizeof(atransport)); D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb, @@ -964,6 +1067,9 @@ void register_usb_transport(usb_handle *usb, const char *serial, unsigned writea if(serial) { t->serial = strdup(serial); } + if(devpath) { + t->devpath = strdup(devpath); + } register_transport(t); } diff --git a/adb/transport_local.c b/adb/transport_local.c index 4431ba7..96a24ba 100644 --- a/adb/transport_local.c +++ b/adb/transport_local.c @@ -185,6 +185,117 @@ static void *server_socket_thread(void * arg) return 0; } +/* This is relevant only for ADB daemon running inside the emulator. */ +#if !ADB_HOST +/* + * 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 worker thread that monitors host connections, and registers a transport for + * every new host connection. This thread replaces server_socket_thread on + * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD + * pipe to communicate with adbd daemon inside the guest. This is done in order + * to provide more robust communication channel between ADB host and guest. The + * main issue with server_socket_thread approach is that it runs on top of TCP, + * and thus is sensitive to network disruptions. For instance, the + * ConnectionManager may decide to reset all network connections, in which case + * the connection between ADB host and guest will be lost. To make ADB traffic + * independent from the network, we use here 'adb' QEMUD service to transfer data + * between the host, and the guest. See external/qemu/android/adb-*.* that + * implements the emulator's side of the protocol. Another advantage of using + * QEMUD approach is that ADB will be up much sooner, since it doesn't depend + * anymore on network being set up. + * The guest side of the protocol contains the following phases: + * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service + * is opened, and it becomes clear whether or not emulator supports that + * protocol. + * - Wait for the ADB host to create connection with the guest. This is done by + * sending an 'accept' request to the adb QEMUD service, and waiting on + * response. + * - When new ADB host connection is accepted, the connection with adb QEMUD + * service is registered as the transport, and a 'start' request is sent to the + * adb QEMUD service, indicating that the guest is ready to receive messages. + * Note that the guest will ignore messages sent down from the emulator before + * the transport registration is completed. That's why we need to send the + * 'start' request after the transport is registered. + */ +static void *qemu_socket_thread(void * arg) +{ +/* 'accept' request to the adb QEMUD service. */ +static const char _accept_req[] = "accept"; +/* 'start' request to the adb QEMUD service. */ +static const char _start_req[] = "start"; +/* 'ok' reply from the adb QEMUD service. */ +static const char _ok_resp[] = "ok"; + + const int port = (int)arg; + int res, fd; + char tmp[256]; + char con_name[32]; + + D("transport: qemu_socket_thread() starting\n"); + + /* adb QEMUD service connection request. */ + snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port); + + /* Connect to the adb QEMUD service. */ + fd = qemu_pipe_open(con_name); + if (fd < 0) { + /* This could be an older version of the emulator, that doesn't + * implement adb QEMUD service. Fall back to the old TCP way. */ + adb_thread_t thr; + D("adb service is not available. Falling back to TCP socket.\n"); + adb_thread_create(&thr, server_socket_thread, arg); + return 0; + } + + for(;;) { + /* + * Wait till the host creates a new connection. + */ + + /* Send the 'accept' request. */ + res = adb_write(fd, _accept_req, strlen(_accept_req)); + if ((size_t)res == strlen(_accept_req)) { + /* Wait for the response. In the response we expect 'ok' on success, + * or 'ko' on failure. */ + res = adb_read(fd, tmp, sizeof(tmp)); + if (res != 2 || memcmp(tmp, _ok_resp, 2)) { + D("Accepting ADB host connection has failed.\n"); + adb_close(fd); + } else { + /* Host is connected. Register the transport, and start the + * exchange. */ + register_socket_transport(fd, "host", port, 1); + adb_write(fd, _start_req, strlen(_start_req)); + } + + /* Prepare for accepting of the next ADB host connection. */ + fd = qemu_pipe_open(con_name); + if (fd < 0) { + D("adb service become unavailable.\n"); + return 0; + } + } else { + D("Unable to send the '%s' request to ADB service.\n", _accept_req); + return 0; + } + } + D("transport: qemu_socket_thread() exiting\n"); + return 0; +} +#endif // !ADB_HOST + void local_init(int port) { adb_thread_t thr; @@ -193,7 +304,21 @@ void local_init(int port) if(HOST) { func = client_socket_thread; } else { +#if ADB_HOST func = server_socket_thread; +#else + /* For the adbd daemon in the system image we need to distinguish + * between the device, and the emulator. */ + char is_qemu[PROPERTY_VALUE_MAX]; + property_get("ro.kernel.qemu", is_qemu, ""); + if (!strcmp(is_qemu, "1")) { + /* Running inside the emulator: use QEMUD pipe as the transport. */ + func = qemu_socket_thread; + } else { + /* Running inside the device: use TCP socket as the transport. */ + func = server_socket_thread; + } +#endif // !ADB_HOST } D("transport: local %s init\n", HOST ? "client" : "server"); diff --git a/adb/usb_libusb.c b/adb/usb_libusb.c index 8c75266..06ff5dc 100644 --- a/adb/usb_libusb.c +++ b/adb/usb_libusb.c @@ -347,7 +347,7 @@ register_device(struct usb_handle *uh, const char *serial) adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial, 1); + register_usb_transport(usb, serial, NULL, 1); return (1); } diff --git a/adb/usb_linux.c b/adb/usb_linux.c index 4d55b74..7bf2057 100644 --- a/adb/usb_linux.c +++ b/adb/usb_linux.c @@ -116,7 +116,8 @@ static void kick_disconnected_devices() } -static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out, +static void register_device(const char *dev_name, const char *devpath, + unsigned char ep_in, unsigned char ep_out, int ifc, int serial_index, unsigned zero_mask); static inline int badname(const char *name) @@ -129,7 +130,7 @@ static inline int badname(const char *name) static void find_usb_device(const char *base, void (*register_device_callback) - (const char *, unsigned char, unsigned char, int, int, unsigned)) + (const char *, const char *, unsigned char, unsigned char, int, int, unsigned)) { char busname[32], devname[32]; unsigned char local_ep_in, local_ep_out; @@ -227,6 +228,11 @@ static void find_usb_device(const char *base, is_adb_interface(vid, pid, interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol)) { + struct stat st; + char pathbuf[128]; + char link[256]; + char *devpath = NULL; + DBGX("looking for bulk endpoints\n"); // looks like ADB... ep1 = (struct usb_endpoint_descriptor *)bufptr; @@ -263,7 +269,26 @@ static void find_usb_device(const char *base, local_ep_out = ep1->bEndpointAddress; } - register_device_callback(devname, local_ep_in, local_ep_out, + // Determine the device path + if (!fstat(fd, &st) && S_ISCHR(st.st_mode)) { + char *slash; + ssize_t link_len; + snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(pathbuf, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) { + snprintf(pathbuf, sizeof(pathbuf), + "usb:%s", slash + 1); + devpath = pathbuf; + } + } + } + + register_device_callback(devname, devpath, + local_ep_in, local_ep_out, interface->bInterfaceNumber, device->iSerialNumber, zero_mask); break; } @@ -532,7 +557,7 @@ int usb_close(usb_handle *h) return 0; } -static void register_device(const char *dev_name, +static void register_device(const char *dev_name, const char *devpath, unsigned char ep_in, unsigned char ep_out, int interface, int serial_index, unsigned zero_mask) { @@ -644,7 +669,7 @@ static void register_device(const char *dev_name, usb->next->prev = usb; adb_mutex_unlock(&usb_lock); - register_usb_transport(usb, serial, usb->writeable); + register_usb_transport(usb, serial, devpath, usb->writeable); return; fail: diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c index 635fa4b..fb1dad0 100644 --- a/adb/usb_linux_client.c +++ b/adb/usb_linux_client.c @@ -19,6 +19,8 @@ #include <unistd.h> #include <string.h> +#include <linux/usb/ch9.h> +#include <linux/usb/functionfs.h> #include <sys/ioctl.h> #include <sys/types.h> #include <dirent.h> @@ -29,20 +31,122 @@ #define TRACE_TAG TRACE_USB #include "adb.h" +#define MAX_PACKET_SIZE_FS 64 +#define MAX_PACKET_SIZE_HS 512 + +#define cpu_to_le16(x) htole16(x) +#define cpu_to_le32(x) htole32(x) struct usb_handle { - int fd; adb_cond_t notify; adb_mutex_t lock; + + int (*write)(usb_handle *h, const void *data, int len); + int (*read)(usb_handle *h, void *data, int len); + void (*kick)(usb_handle *h); + + // Legacy f_adb + int fd; + + // FunctionFS + int control; + int bulk_out; /* "out" from the host's perspective => source for adbd */ + int bulk_in; /* "in" from the host's perspective => sink for adbd */ +}; + +static const struct { + struct usb_functionfs_descs_head header; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio source; + struct usb_endpoint_descriptor_no_audio sink; + } __attribute__((packed)) fs_descs, hs_descs; +} __attribute__((packed)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .length = cpu_to_le32(sizeof(descriptors)), + .fs_count = 3, + .hs_count = 3, + }, + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.fs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + .sink = { + .bLength = sizeof(descriptors.fs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + }, + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.hs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + .sink = { + .bLength = sizeof(descriptors.hs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + }, +}; + +#define STR_INTERFACE_ "ADB Interface" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE_)]; + } __attribute__((packed)) lang0; +} __attribute__((packed)) strings = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = cpu_to_le32(sizeof(strings)), + .str_count = cpu_to_le32(1), + .lang_count = cpu_to_le32(1), + }, + .lang0 = { + cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE_, + }, }; -void usb_cleanup() -{ - // nothing to do here -} -static void *usb_open_thread(void *x) + +static void *usb_adb_open_thread(void *x) { struct usb_handle *usb = (struct usb_handle *)x; int fd; @@ -72,14 +176,14 @@ static void *usb_open_thread(void *x) usb->fd = fd; D("[ usb_thread - registering device ]\n"); - register_usb_transport(usb, 0, 1); + register_usb_transport(usb, 0, 0, 1); } // never gets here return 0; } -int usb_write(usb_handle *h, const void *data, int len) +static int usb_adb_write(usb_handle *h, const void *data, int len) { int n; @@ -94,7 +198,7 @@ int usb_write(usb_handle *h, const void *data, int len) return 0; } -int usb_read(usb_handle *h, void *data, int len) +static int usb_adb_read(usb_handle *h, void *data, int len) { int n; @@ -109,14 +213,31 @@ int usb_read(usb_handle *h, void *data, int len) return 0; } -void usb_init() +static void usb_adb_kick(usb_handle *h) +{ + D("usb_kick\n"); + adb_mutex_lock(&h->lock); + adb_close(h->fd); + h->fd = -1; + + // notify usb_adb_open_thread that we are disconnected + adb_cond_signal(&h->notify); + adb_mutex_unlock(&h->lock); +} + +static void usb_adb_init() { usb_handle *h; adb_thread_t tid; int fd; h = calloc(1, sizeof(usb_handle)); + + h->write = usb_adb_write; + h->read = usb_adb_read; + h->kick = usb_adb_kick; h->fd = -1; + adb_cond_init(&h->notify, 0); adb_mutex_init(&h->lock, 0); @@ -133,25 +254,239 @@ void usb_init() } D("[ usb_init - starting thread ]\n"); - if(adb_thread_create(&tid, usb_open_thread, h)){ + if(adb_thread_create(&tid, usb_adb_open_thread, h)){ fatal_errno("cannot create usb thread"); } } -void usb_kick(usb_handle *h) + +static void init_functionfs(struct usb_handle *h) { - D("usb_kick\n"); + ssize_t ret; + + D("OPENING %s\n", USB_FFS_ADB_EP0); + h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR); + if (h->control < 0) { + D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &descriptors, sizeof(descriptors)); + if (ret < 0) { + D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &strings, sizeof(strings)); + if (ret < 0) { + D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR); + if (h->bulk_out < 0) { + D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno); + goto err; + } + + h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR); + if (h->bulk_in < 0) { + D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno); + goto err; + } + + return; + +err: + if (h->bulk_in > 0) { + adb_close(h->bulk_in); + h->bulk_in = -1; + } + if (h->bulk_out > 0) { + adb_close(h->bulk_out); + h->bulk_out = -1; + } + if (h->control > 0) { + adb_close(h->control); + h->control = -1; + } + return; +} + +static void *usb_ffs_open_thread(void *x) +{ + struct usb_handle *usb = (struct usb_handle *)x; + + while (1) { + // wait until the USB device needs opening + adb_mutex_lock(&usb->lock); + while (usb->control != -1) + adb_cond_wait(&usb->notify, &usb->lock); + adb_mutex_unlock(&usb->lock); + + while (1) { + init_functionfs(usb); + + if (usb->control >= 0) + break; + + adb_sleep_ms(1000); + } + + D("[ usb_thread - registering device ]\n"); + register_usb_transport(usb, 0, 0, 1); + } + + // never gets here + return 0; +} + +static int bulk_write(int bulk_in, const char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_write(bulk_in, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) + return ret; + } else { + count += ret; + } + } while (count < length); + + D("[ bulk_write done fd=%d ]\n", bulk_in); + return count; +} + +static int usb_ffs_write(usb_handle *h, const void *data, int len) +{ + int n; + + D("about to write (fd=%d, len=%d)\n", h->bulk_in, len); + n = bulk_write(h->bulk_in, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_in, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_in); + return 0; +} + +static int bulk_read(int bulk_out, char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_read(bulk_out, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) { + D("[ bulk_read failed fd=%d length=%d count=%d ]\n", + bulk_out, length, count); + return ret; + } + } else { + count += ret; + } + } while (count < length); + + return count; +} + +static int usb_ffs_read(usb_handle *h, void *data, int len) +{ + int n; + + D("about to read (fd=%d, len=%d)\n", h->bulk_out, len); + n = bulk_read(h->bulk_out, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_out, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_out); + return 0; +} + +static void usb_ffs_kick(usb_handle *h) +{ + int err; + + err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno); + + err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno); + adb_mutex_lock(&h->lock); - adb_close(h->fd); - h->fd = -1; + adb_close(h->control); + adb_close(h->bulk_out); + adb_close(h->bulk_in); + h->control = h->bulk_out = h->bulk_in = -1; - // notify usb_open_thread that we are disconnected + // notify usb_ffs_open_thread that we are disconnected adb_cond_signal(&h->notify); adb_mutex_unlock(&h->lock); } +static void usb_ffs_init() +{ + usb_handle *h; + adb_thread_t tid; + + D("[ usb_init - using FunctionFS ]\n"); + + h = calloc(1, sizeof(usb_handle)); + + h->write = usb_ffs_write; + h->read = usb_ffs_read; + h->kick = usb_ffs_kick; + + h->control = -1; + h->bulk_out = -1; + h->bulk_out = -1; + + adb_cond_init(&h->notify, 0); + adb_mutex_init(&h->lock, 0); + + D("[ usb_init - starting thread ]\n"); + if (adb_thread_create(&tid, usb_ffs_open_thread, h)){ + fatal_errno("[ cannot create usb thread ]\n"); + } +} + +void usb_init() +{ + if (access(USB_FFS_ADB_EP0, F_OK) == 0) + usb_ffs_init(); + else + usb_adb_init(); +} + +void usb_cleanup() +{ +} + +int usb_write(usb_handle *h, const void *data, int len) +{ + return h->write(h, data, len); +} + +int usb_read(usb_handle *h, void *data, int len) +{ + return h->read(h, data, len); +} int usb_close(usb_handle *h) { - // nothing to do here return 0; } + +void usb_kick(usb_handle *h) +{ + h->kick(h); +} diff --git a/adb/usb_osx.c b/adb/usb_osx.c index 00d02da..45ce444 100644 --- a/adb/usb_osx.c +++ b/adb/usb_osx.c @@ -125,10 +125,13 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) IOUSBDeviceInterface197 **dev = NULL; HRESULT result; SInt32 score; + UInt32 locationId; UInt16 vendor; UInt16 product; UInt8 serialIndex; char serial[256]; + char devpathBuf[64]; + char *devpath = NULL; while ((usbInterface = IOIteratorNext(iterator))) { //* Create an intermediate interface plugin @@ -192,6 +195,11 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) kr = (*dev)->GetDeviceVendor(dev, &vendor); kr = (*dev)->GetDeviceProduct(dev, &product); + kr = (*dev)->GetLocationID(dev, &locationId); + if (kr == 0) { + snprintf(devpathBuf, sizeof(devpathBuf), "usb:%lX", locationId); + devpath = devpathBuf; + } kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); if (serialIndex > 0) { @@ -256,7 +264,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) } DBG("AndroidDeviceAdded calling register_usb_transport\n"); - register_usb_transport(handle, (serial[0] ? serial : NULL), 1); + register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1); // Register for an interest notification of this device being removed. // Pass the reference to our private data as the refCon for the diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c index 7ae6372..e132c67 100644 --- a/adb/usb_vendors.c +++ b/adb/usb_vendors.c @@ -113,6 +113,46 @@ #define VENDOR_ID_ARCHOS 0x0E79 // Positivo's USB Vendor ID #define VENDOR_ID_POSITIVO 0x1662 +// Fujitsu's USB Vendor ID +#define VENDOR_ID_FUJITSU 0x04C5 +// Lumigon's USB Vendor ID +#define VENDOR_ID_LUMIGON 0x25E3 +// Quanta's USB Vendor ID +#define VENDOR_ID_QUANTA 0x0408 +// INQ Mobile's USB Vendor ID +#define VENDOR_ID_INQ_MOBILE 0x2314 +// Sony's USB Vendor ID +#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 +// Kobo's USB Vendor ID +#define VENDOR_ID_KOBO 0x2237 +// Teleepoch's USB Vendor ID +#define VENDOR_ID_TELEEPOCH 0x2340 +// AnyDATA's USB Vendor ID +#define VENDOR_ID_ANYDATA 0x16D5 +// Harris's USB Vendor ID +#define VENDOR_ID_HARRIS 0x19A5 +// OPPO's USB Vendor ID +#define VENDOR_ID_OPPO 0x22D9 +// Xiaomi's USB Vendor ID +#define VENDOR_ID_XIAOMI 0x2717 +// BYD's USB Vendor ID +#define VENDOR_ID_BYD 0x19D1 +// OUYA's USB Vendor ID +#define VENDOR_ID_OUYA 0x2836 +// Haier's USB Vendor ID +#define VENDOR_ID_HAIER 0x201E +// Hisense's USB Vendor ID +#define VENDOR_ID_HISENSE 0x109b +// MTK's USB Vendor ID +#define VENDOR_ID_MTK 0x0e8d +// B&N Nook's USB Vendor ID +#define VENDOR_ID_NOOK 0x2080 +// Qisda's USB Vendor ID +#define VENDOR_ID_QISDA 0x1D45 /** built-in vendor list */ @@ -156,6 +196,26 @@ int builtInVendorIds[] = { VENDOR_ID_PEGATRON, VENDOR_ID_ARCHOS, VENDOR_ID_POSITIVO, + VENDOR_ID_FUJITSU, + VENDOR_ID_LUMIGON, + VENDOR_ID_QUANTA, + VENDOR_ID_INQ_MOBILE, + VENDOR_ID_SONY, + VENDOR_ID_LAB126, + VENDOR_ID_YULONG_COOLPAD, + VENDOR_ID_KOBO, + VENDOR_ID_TELEEPOCH, + VENDOR_ID_ANYDATA, + VENDOR_ID_HARRIS, + VENDOR_ID_OPPO, + VENDOR_ID_XIAOMI, + VENDOR_ID_BYD, + VENDOR_ID_OUYA, + VENDOR_ID_HAIER, + VENDOR_ID_HISENSE, + VENDOR_ID_MTK, + VENDOR_ID_NOOK, + VENDOR_ID_QISDA, }; #define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) @@ -208,6 +268,7 @@ void usb_vendors_init(void) break; } } + fclose(f); } } } diff --git a/adb/usb_windows.c b/adb/usb_windows.c index b216999..4936b77 100644 --- a/adb/usb_windows.c +++ b/adb/usb_windows.c @@ -255,7 +255,7 @@ usb_handle* do_usb_open(const wchar_t* interface_name) { } int usb_write(usb_handle* handle, const void* data, int len) { - unsigned long time_out = 500 + len * 8; + unsigned long time_out = 5000; unsigned long written = 0; int ret; @@ -300,7 +300,7 @@ int usb_write(usb_handle* handle, const void* data, int len) { } int usb_read(usb_handle *handle, void* data, int len) { - unsigned long time_out = 500 + len * 8; + unsigned long time_out = 0; unsigned long read = 0; int ret; @@ -322,7 +322,7 @@ int usb_read(usb_handle *handle, void* data, int len) { if (len == 0) return 0; - } else if (saved_errno != ERROR_SEM_TIMEOUT) { + } else { // assume ERROR_INVALID_HANDLE indicates we are disconnected if (saved_errno == ERROR_INVALID_HANDLE) usb_kick(handle); @@ -490,7 +490,7 @@ void find_devices() { true)) { // Lets make sure that we don't duplicate this device if (register_new_device(handle)) { - register_usb_transport(handle, serial_number, 1); + register_usb_transport(handle, serial_number, NULL, 1); } else { D("register_new_device failed for %s\n", interf_name); usb_cleanup_handle(handle); diff --git a/charger/Android.mk b/charger/Android.mk index 5367a98..0258604 100644 --- a/charger/Android.mk +++ b/charger/Android.mk @@ -8,6 +8,14 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ charger.c +ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) +LOCAL_CFLAGS := -DCHARGER_DISABLE_INIT_BLANK +endif + +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND +endif + LOCAL_MODULE := charger LOCAL_MODULE_TAGS := optional LOCAL_FORCE_STATIC_EXECUTABLE := true @@ -17,7 +25,10 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) LOCAL_C_INCLUDES := bootable/recovery LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng -LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libc +ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) +LOCAL_STATIC_LIBRARIES += libsuspend +endif +LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils libm libc include $(BUILD_EXECUTABLE) diff --git a/charger/charger.c b/charger/charger.c index abf5517..353bdf0 100644 --- a/charger/charger.c +++ b/charger/charger.c @@ -21,25 +21,30 @@ #include <errno.h> #include <fcntl.h> #include <linux/input.h> -#include <linux/netlink.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/poll.h> -#include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/un.h> #include <time.h> #include <unistd.h> +#include <sys/socket.h> +#include <linux/netlink.h> + #include <cutils/android_reboot.h> #include <cutils/klog.h> #include <cutils/list.h> #include <cutils/misc.h> #include <cutils/uevent.h> +#ifdef CHARGER_ENABLE_SUSPEND +#include <suspend/autosuspend.h> +#endif + #include "minui/minui.h" #ifndef max @@ -351,6 +356,21 @@ static void remove_supply(struct charger *charger, struct power_supply *supply) free(supply); } +#ifdef CHARGER_ENABLE_SUSPEND +static int request_suspend(bool enable) +{ + if (enable) + return autosuspend_enable(); + else + return autosuspend_disable(); +} +#else +static int request_suspend(bool enable) +{ + return 0; +} +#endif + static void parse_uevent(const char *msg, struct uevent *uevent) { uevent->action = ""; @@ -684,6 +704,8 @@ static void update_screen_state(struct charger *charger, int64_t now) charger->next_screen_transition = -1; gr_fb_blank(true); LOGV("[%lld] animation done\n", now); + if (charger->num_supplies_online > 0) + request_suspend(true); return; } @@ -823,8 +845,10 @@ static void process_key(struct charger *charger, int code, int64_t now) } } else { /* if the power key got released, force screen state cycle */ - if (key->pending) + if (key->pending) { + request_suspend(false); kick_animation(charger->batt_anim); + } } } @@ -842,6 +866,7 @@ static void handle_input_state(struct charger *charger, int64_t now) static void handle_power_supply_state(struct charger *charger, int64_t now) { if (charger->num_supplies_online == 0) { + request_suspend(false); if (charger->next_pwr_check == -1) { charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n", @@ -974,7 +999,9 @@ int main(int argc, char **argv) ev_sync_key_state(set_key_callback, charger); +#ifndef CHARGER_DISABLE_INIT_BLANK gr_fb_blank(true); +#endif charger->next_screen_transition = now - 1; charger->next_key_check = -1; diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c index f672336..3569e27 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,52 @@ 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); + uint64_t capabilities; + 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, &capabilities); + } } static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize) @@ -79,7 +120,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 +276,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..3fca64f 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -1,16 +1,19 @@ # Copyright 2005 The Android Open Source Project -ifneq ($(filter arm x86,$(TARGET_ARCH)),) +ifneq ($(filter arm mips 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_SRC_FILES:= \ + backtrace.c \ + debuggerd.c \ + getevent.c \ + tombstone.c \ + utility.c \ + $(TARGET_ARCH)/machine.c -LOCAL_CFLAGS := -Wall +LOCAL_CFLAGS := -Wall -Wno-unused-parameter -std=gnu99 LOCAL_MODULE := debuggerd ifeq ($(ARCH_ARM_HAVE_VFP),true) @@ -20,7 +23,11 @@ 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 \ + libselinux include $(BUILD_EXECUTABLE) @@ -30,6 +37,7 @@ LOCAL_SRC_FILES += $(TARGET_ARCH)/crashglue.S LOCAL_MODULE := crasher LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS += -fstack-protector-all #LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) diff --git a/debuggerd/arm/machine.c b/debuggerd/arm/machine.c index d5efb79..160db7b 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; @@ -140,7 +53,8 @@ static void dump_memory(int tfd, int pid, uintptr_t addr, /* catch underflow */ p = 0; } - end = p + 80; + /* Dump more memory content for the crashing thread. */ + end = p + 256; /* catch overflow; 'end - p' has to be multiples of 16 */ while (end < p) end -= 16; @@ -165,9 +79,11 @@ 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); + /* Enable the following code blob to dump ASCII values */ +#if 0 int j; for (j = 0; j < 4; j++) { /* @@ -182,37 +98,31 @@ static void dump_memory(int tfd, int pid, uintptr_t addr, *asc_out++ = '.'; } } +#endif 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 +132,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); + _LOG(log, false, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, at_fault); } - } 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]; - } - } - 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..ba76e7d --- /dev/null +++ b/debuggerd/backtrace.c @@ -0,0 +1,148 @@ +/* + * 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 != NULL) { + struct dirent* de = NULL; + while ((de = readdir(d)) != NULL) { + 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/crasher.c b/debuggerd/crasher.c index 00652e9..8c225cb 100644 --- a/debuggerd/crasher.c +++ b/debuggerd/crasher.c @@ -17,9 +17,11 @@ #include <cutils/sockets.h> +extern const char* __progname; + void crash1(void); void crashnostack(void); -void maybeabort(void); +static int do_action(const char* arg); static void debuggerd_connect() { @@ -27,30 +29,44 @@ static void debuggerd_connect() int s; sprintf(tmp, "%d", gettid()); s = socket_local_client("android:debuggerd", - ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); if(s >= 0) { read(s, tmp, 1); close(s); } } -void test_call1() -{ - *((int*) 32) = 1; +static void maybeabort() { + if(time(0) != 42) { + abort(); + } } -void *test_thread(void *x) -{ - printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid()); +static int smash_stack(int i) { + printf("crasher: deliberately corrupting stack...\n"); + // Unless there's a "big enough" buffer on the stack, gcc + // doesn't bother inserting checks. + char buf[8]; + // If we don't write something relatively unpredicatable + // into the buffer and then do something with it, gcc + // optimizes everything away and just returns a constant. + *(int*)(&buf[7]) = (uintptr_t) &buf[0]; + return *(int*)(&buf[0]); +} - sleep(1); - test_call1(); - printf("goodbye\n"); +__attribute__((noinline)) static void overflow_stack(void* p) { + fprintf(stderr, "p = %p\n", p); + void* buf[1]; + buf[0] = p; + overflow_stack(&buf); +} - return 0; +static void test_call1() +{ + *((int*) 32) = 1; } -void *noisy(void *x) +static void *noisy(void *x) { char c = (unsigned) x; for(;;) { @@ -61,7 +77,7 @@ void *noisy(void *x) return 0; } -int ctest() +static int ctest() { pthread_t thr; pthread_attr_t attr; @@ -74,33 +90,89 @@ int ctest() return 0; } -int main(int argc, char **argv) +static void* thread_callback(void* raw_arg) { - pthread_t thr; - pthread_attr_t attr; + return (void*) do_action((const char*) raw_arg); +} + +static int do_action_on_thread(const char* arg) +{ + pthread_t t; + pthread_create(&t, NULL, thread_callback, (void*) arg); + void* result = NULL; + pthread_join(t, &result); + return (int) result; +} + +__attribute__((noinline)) static int crash3(int a) { + *((int*) 0xdead) = a; + return a*4; +} - fprintf(stderr,"crasher: " __TIME__ "!@\n"); +__attribute__((noinline)) static int crash2(int a) { + a = crash3(a) + 2; + return a*3; +} + +__attribute__((noinline)) static int crash(int a) { + a = crash2(a) + 1; + return a*2; +} + +static void abuse_heap() { + char buf[16]; + free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately. +} + +static int do_action(const char* arg) +{ fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid()); + if (!strncmp(arg, "thread-", strlen("thread-"))) { + return do_action_on_thread(arg + strlen("thread-")); + } else if (!strcmp(arg,"smash-stack")) { + return smash_stack(42); + } else if (!strcmp(arg,"stack-overflow")) { + overflow_stack(NULL); + } else if (!strcmp(arg,"nostack")) { + crashnostack(); + } else if (!strcmp(arg,"ctest")) { + return ctest(); + } else if (!strcmp(arg,"exit")) { + exit(1); + } else if (!strcmp(arg,"crash")) { + return crash(42); + } else if (!strcmp(arg,"abort")) { + maybeabort(); + } else if (!strcmp(arg, "heap-usage")) { + abuse_heap(); + } + + fprintf(stderr, "%s OP\n", __progname); + fprintf(stderr, "where OP is:\n"); + fprintf(stderr, " smash-stack overwrite a stack-guard canary\n"); + fprintf(stderr, " stack-overflow recurse until the stack overflows\n"); + fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n"); + fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n"); + fprintf(stderr, " nostack crash with a NULL stack pointer\n"); + fprintf(stderr, " ctest (obsoleted by thread-crash?)\n"); + fprintf(stderr, " exit call exit(1)\n"); + fprintf(stderr, " crash cause a SIGSEGV\n"); + fprintf(stderr, " abort call abort()\n"); + fprintf(stderr, "prefix any of the above with 'thread-' to not run\n"); + fprintf(stderr, "on the process' main thread.\n"); + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + fprintf(stderr,"crasher: built at " __TIME__ "!@\n"); + if(argc > 1) { - if(!strcmp(argv[1],"nostack")) crashnostack(); - if(!strcmp(argv[1],"ctest")) return ctest(); - if(!strcmp(argv[1],"exit")) exit(1); - if(!strcmp(argv[1],"abort")) maybeabort(); - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&thr, &attr, test_thread, 0); - while(1) sleep(1); + return do_action(argv[1]); } else { crash1(); -// *((int*) 0) = 42; } - - return 0; -} -void maybeabort() -{ - if(time(0) != 42) abort(); + return 0; } diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 2acf26d..99e6f13 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -23,591 +23,37 @@ #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; + uintptr_t abort_msg_address; +} debugger_request_t; static int write_string(const char* file, const char* string) @@ -654,25 +100,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 +127,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 +148,277 @@ 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; + memset(&msg, 0, sizeof(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(debugger_msg_t)) { + XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); + } else { + 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; + out_request->abort_msg_address = msg.abort_msg_address; + + 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; + memset(&request, 0, sizeof(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, request.abort_msg_address, 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: +#ifdef SIGSTKFLT + case SIGSTKFLT: +#endif + { + 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, request.abort_msg_address, !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"); + XLOG("detaching\n"); + if (attach_gdb) { + /* stop the process so we can debug */ + kill(request.pid, SIGSTOP); - /* stop the process so we can debug */ - kill(cr.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 +432,10 @@ int main() signal(SIGBUS, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGSEGV, SIG_DFL); - signal(SIGSTKFLT, SIG_DFL); signal(SIGPIPE, SIG_DFL); +#ifdef SIGSTKFLT + signal(SIGSTKFLT, SIG_DFL); +#endif logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); @@ -929,9 +451,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 +473,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/mips/crashglue.S b/debuggerd/mips/crashglue.S new file mode 100644 index 0000000..70a6641 --- /dev/null +++ b/debuggerd/mips/crashglue.S @@ -0,0 +1,48 @@ + .set noat + + .globl crash1 + .globl crashnostack + +crash1: + li $0,0xdead0000+0 + li $1,0xdead0000+1 + li $2,0xdead0000+2 + li $3,0xdead0000+3 + li $4,0xdead0000+4 + li $5,0xdead0000+5 + li $6,0xdead0000+6 + li $7,0xdead0000+7 + li $8,0xdead0000+8 + li $9,0xdead0000+9 + li $10,0xdead0000+10 + li $11,0xdead0000+11 + li $12,0xdead0000+12 + li $13,0xdead0000+13 + li $14,0xdead0000+14 + li $15,0xdead0000+15 + li $16,0xdead0000+16 + li $17,0xdead0000+17 + li $18,0xdead0000+18 + li $19,0xdead0000+19 + li $20,0xdead0000+20 + li $21,0xdead0000+21 + li $22,0xdead0000+22 + li $23,0xdead0000+23 + li $24,0xdead0000+24 + li $25,0xdead0000+25 + li $26,0xdead0000+26 + li $27,0xdead0000+27 + li $28,0xdead0000+28 + # don't trash the stack otherwise the signal handler won't run + #li $29,0xdead0000+29 + li $30,0xdead0000+30 + li $31,0xdead0000+31 + + lw $zero,($0) + b . + + +crashnostack: + li $sp, 0 + lw $zero,($0) + b . diff --git a/debuggerd/mips/machine.c b/debuggerd/mips/machine.c new file mode 100644 index 0000000..dba1711 --- /dev/null +++ b/debuggerd/mips/machine.c @@ -0,0 +1,178 @@ +/* system/debuggerd/debuggerd.c +** +** Copyright 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 <errno.h> +#include <sys/types.h> +#include <sys/ptrace.h> + +#include <corkscrew/ptrace.h> + +#include <linux/user.h> + +#include "../utility.h" +#include "../machine.h" + +/* enable to dump memory pointed to by every register */ +#define DUMP_MEMORY_FOR_ALL_REGISTERS 1 + +#define R(x) ((unsigned int)(x)) + +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; + + p = addr & ~3; + p -= 32; + if (p > addr) { + /* catch underflow */ + p = 0; + } + end = p + 80; + /* catch overflow; 'end - p' has to be multiples of 16 */ + while (end < p) + end -= 16; + + /* Dump the code around PC as: + * addr contents ascii + * 00008d34 ef000000 e8bd0090 e1b00000 512fff1e ............../Q + * 00008d44 ea00b1f9 e92d0090 e3a070fc ef000000 ......-..p...... + */ + while (p < end) { + char* asc_out = ascii_buffer; + + sprintf(code_buffer, "%08x ", p); + + int i; + for (i = 0; i < 4; i++) { + /* + * If we see (data == -1 && errno != 0), we know that the ptrace + * call failed, probably because we're dumping memory in an + * unmapped or inaccessible page. I don't know if there's + * value in making that explicit in the output -- it likely + * just complicates parsing and clarifies nothing for the + * enlightened reader. + */ + long data = ptrace(PTRACE_PEEKTEXT, tid, (void*)p, NULL); + sprintf(code_buffer + strlen(code_buffer), "%08lx ", data); + + int j; + for (j = 0; j < 4; j++) { + /* + * Our isprint() allows high-ASCII characters that display + * differently (often badly) in different viewers, so we + * just use a simpler test. + */ + char val = (data >> (j*8)) & 0xff; + if (val >= 0x20 && val < 0x7f) { + *asc_out++ = val; + } else { + *asc_out++ = '.'; + } + } + p += 4; + } + *asc_out = '\0'; + _LOG(log, !at_fault, " %s %s\n", code_buffer, ascii_buffer); + } +} + +/* + * 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) { + pt_regs_mips_t r; + if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { + return; + } + + if (at_fault && DUMP_MEMORY_FOR_ALL_REGISTERS) { + static const char REG_NAMES[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; + + for (int reg = 0; reg < 32; reg++) { + /* skip uninteresting registers */ + if (reg == 0 /* $0 */ + || reg == 26 /* $k0 */ + || reg == 27 /* $k1 */ + || reg == 31 /* $ra (done below) */ + ) + continue; + + uintptr_t addr = R(r.regs[reg]); + + /* + * Don't bother if it looks like a small int or ~= null, or if + * it's in the kernel area. + */ + if (addr < 4096 || addr >= 0x80000000) { + continue; + } + + _LOG(log, false, "\nmemory near %.2s:\n", ®_NAMES[reg * 2]); + dump_memory(log, tid, addr, at_fault); + } + } + + unsigned int pc = R(r.cp0_epc); + unsigned int ra = R(r.regs[31]); + + _LOG(log, !at_fault, "\ncode around pc:\n"); + dump_memory(log, tid, (uintptr_t)pc, at_fault); + + if (pc != ra) { + _LOG(log, !at_fault, "\ncode around ra:\n"); + dump_memory(log, tid, (uintptr_t)ra, at_fault); + } +} + +void dump_registers(const ptrace_context_t* context __attribute((unused)), + log_t* log, pid_t tid, bool at_fault) +{ + pt_regs_mips_t r; + bool only_in_tombstone = !at_fault; + + if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { + _LOG(log, only_in_tombstone, "cannot get registers: %s\n", strerror(errno)); + return; + } + + _LOG(log, only_in_tombstone, " zr %08x at %08x v0 %08x v1 %08x\n", + R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); + _LOG(log, only_in_tombstone, " a0 %08x a1 %08x a2 %08x a3 %08x\n", + R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); + _LOG(log, only_in_tombstone, " t0 %08x t1 %08x t2 %08x t3 %08x\n", + R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); + _LOG(log, only_in_tombstone, " t4 %08x t5 %08x t6 %08x t7 %08x\n", + R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); + _LOG(log, only_in_tombstone, " s0 %08x s1 %08x s2 %08x s3 %08x\n", + R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); + _LOG(log, only_in_tombstone, " s4 %08x s5 %08x s6 %08x s7 %08x\n", + R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); + _LOG(log, only_in_tombstone, " t8 %08x t9 %08x k0 %08x k1 %08x\n", + R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); + _LOG(log, only_in_tombstone, " gp %08x sp %08x s8 %08x ra %08x\n", + R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); + _LOG(log, only_in_tombstone, " hi %08x lo %08x bva %08x epc %08x\n", + R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); +} 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..da5f03c --- /dev/null +++ b/debuggerd/tombstone.c @@ -0,0 +1,777 @@ +/* + * 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> + +#include <selinux/android.h> + +#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"; +#ifdef SIGSTKFLT + case SIGSTKFLT: return "SIGSTKFLT"; +#endif + case SIGSTOP: return "SIGSTOP"; + default: return "?"; + } +} + +static const char *get_sigcode(int signo, int code) +{ + // Try the signal-specific codes... + 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; + case SIGTRAP: + switch (code) { + case TRAP_BRKPT: return "TRAP_BRKPT"; + case TRAP_TRACE: return "TRAP_TRACE"; + } + break; + } + // Then the other codes... + switch (code) { + case SI_USER: return "SI_USER"; +#if defined(SI_KERNEL) + case SI_KERNEL: return "SI_KERNEL"; +#endif + case SI_QUEUE: return "SI_QUEUE"; + case SI_TIMER: return "SI_TIMER"; + case SI_MESGQ: return "SI_MESGQ"; + case SI_ASYNCIO: return "SI_ASYNCIO"; +#if defined(SI_SIGIO) + case SI_SIGIO: return "SI_SIGIO"; +#endif +#if defined(SI_TKILL) + case SI_TKILL: return "SI_TKILL"; +#endif + } + // Then give up... + return "?"; +} + +static void dump_revision_info(log_t* log) +{ + char revision[PROPERTY_VALUE_MAX]; + + property_get("ro.revision", revision, "unknown"); + + _LOG(log, false, "Revision: '%s'\n", revision); +} + +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_map(log_t* log, map_info_t* m, const char* what) { + if (m != NULL) { + _LOG(log, false, " %08x-%08x %c%c%c %s\n", m->start, m->end, + m->is_readable ? 'r' : '-', + m->is_writable ? 'w' : '-', + m->is_executable ? 'x' : '-', + m->name); + } else { + _LOG(log, false, " (no %s)\n", what); + } +} + +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). + */ + dump_map(log, next, "map below"); + dump_map(log, map, "map for address"); + dump_map(log, prev, "map above"); +} + +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* de; + while ((de = readdir(d)) != NULL) { + /* 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); +} + +static void dump_abort_message(log_t* log, pid_t tid, uintptr_t address) { + if (address == 0) { + return; + } + + address += sizeof(size_t); // Skip the buffer length. + + char msg[512]; + memset(msg, 0, sizeof(msg)); + char* p = &msg[0]; + while (p < &msg[sizeof(msg)]) { + uint32_t data; + if (!try_get_word_ptrace(tid, address, &data)) { + break; + } + address += sizeof(uint32_t); + + if ((*p++ = (data >> 0) & 0xff) == 0) { + break; + } + if ((*p++ = (data >> 8) & 0xff) == 0) { + break; + } + if ((*p++ = (data >> 16) & 0xff) == 0) { + break; + } + if ((*p++ = (data >> 24) & 0xff) == 0) { + break; + } + } + msg[sizeof(msg) - 1] = '\0'; + + _LOG(log, false, "Abort message: '%s'\n", msg); +} + +/* + * 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, uintptr_t abort_msg_address, + 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_revision_info(log); + dump_thread_info(log, pid, tid, true); + if (signal) { + dump_fault_addr(log, tid, signal); + } + dump_abort_message(log, tid, abort_msg_address); + + 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, uintptr_t abort_msg_address, + 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); + + if (selinux_android_restorecon(TOMBSTONE_DIR) == -1) { + *detach_failed = false; + return NULL; + } + + 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, abort_msg_address, dump_sibling_threads, + total_sleep_time_usec); + + close(fd); + return path; +} diff --git a/nexus/DhcpListener.h b/debuggerd/tombstone.h index ca6fe37..d4a1a96 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, uintptr_t abort_msg_address, + 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..1189e1f 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -16,9 +16,11 @@ 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 +LOCAL_MODULE_TAGS := debug ifeq ($(HOST_OS),linux) LOCAL_SRC_FILES += usb_linux.c util_linux.c @@ -47,10 +49,23 @@ 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_host \ + libsparse_host \ + libz + +ifneq ($(HOST_OS),windows) +LOCAL_STATIC_LIBRARIES += libselinux +endif # HOST_OS != windows include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) + + +$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) + ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) 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..8d46991 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,34 @@ * 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 <errno.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 + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) double now() { @@ -50,7 +64,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 +74,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 +100,82 @@ 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 } +}; + +/* Return true if this partition is supported by the fastboot format command. + * It is also used to determine if we should first erase a partition before + * flashing it with an ext4 filesystem. See needs_erase() + * + * Not all devices report the filesystem type, so don't report any errors, + * just return false. + */ +int fb_format_supported(usb_handle *usb, const char *partition) +{ + char response[FB_RESPONSE_SZ+1]; + struct generator *generator = NULL; + int status; + unsigned int i; + + status = fb_getvar(usb, response, "partition-type:%s", partition); + if (status) { + return 0; + } + + for (i = 0; i < ARRAY_SIZE(generators); i++) { + if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { + generator = &generators[i]; + break; + } + } + + if (generator) { + return 1; + } + + return 0; +} + static int cb_default(Action *a, int status, char *resp) { if (status) { @@ -133,6 +227,177 @@ 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 + make_ext4fs_sparse_fd(fd, image->partition_size, NULL, 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 +411,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; @@ -316,6 +594,8 @@ int fb_execute_queue(usb_handle *usb) int status = 0; a = action_list; + if (!a) + return status; resp[FB_RESPONSE_SZ] = 0; double start = -1; @@ -340,6 +620,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"); } @@ -348,3 +636,8 @@ int fb_execute_queue(usb_handle *usb) fprintf(stderr,"finished. total time: %.3fs\n", (now() - start)); return status; } + +int fb_queue_is_empty(void) +{ + return (action_list == NULL); +} diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index fe0af7a..3de6d7d 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,9 @@ static const char *product = 0; static const char *cmdline = 0; static int wipe_data = 0; static unsigned short vendor_id = 0; +static int long_listing = 0; +static int64_t sparse_limit = -1; +static int64_t target_sparse_limit = -1; static unsigned base_addr = 0x10000000; @@ -88,6 +103,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,12 +131,33 @@ 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; int sz; int fd; + int errno_tmp; data = 0; fd = open(fn, O_RDONLY); @@ -140,14 +178,21 @@ void *load_file(const char *fn, unsigned *_sz) return data; oops: + errno_tmp = errno; close(fd); if(data != 0) free(data); + errno = errno_tmp; return 0; } #endif int match_fastboot(usb_ifc_info *info) { + return match_fastboot_with_serial(info, serial); +} + +int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial) +{ if(!(vendor_id && (info->dev_vendor == vendor_id)) && (info->dev_vendor != 0x18d1) && // Google (info->dev_vendor != 0x8087) && // Intel @@ -158,20 +203,23 @@ int match_fastboot(usb_ifc_info *info) (info->dev_vendor != 0x22b8) && // Motorola (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; if(info->ifc_subclass != 0x42) return -1; if(info->ifc_protocol != 0x03) return -1; - // require matching serial number if a serial number is specified + // require matching serial number or device path if requested // at the command line with the -s option. - if (serial && strcmp(serial, info->serial_number) != 0) return -1; + if (local_serial && (strcmp(local_serial, info->serial_number) != 0 && + strcmp(local_serial, info->device_path) != 0)) return -1; return 0; } int list_devices_callback(usb_ifc_info *info) { - if (match_fastboot(info) == 0) { + if (match_fastboot_with_serial(info, NULL) == 0) { char* serial = info->serial_number; if (!info->writable) { serial = "no permissions"; // like "adb devices" @@ -180,7 +228,13 @@ int list_devices_callback(usb_ifc_info *info) serial = "????????????"; } // output compatible with "adb devices" - printf("%s\tfastboot\n", serial); + if (!long_listing) { + printf("%s\tfastboot\n", serial); + } else if (!info->device_path) { + printf("%-22s fastboot\n", serial); + } else { + printf("%-22s fastboot %s\n", serial, info->device_path); + } } return -1; @@ -222,6 +276,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" @@ -232,13 +287,20 @@ void usage(void) " help show this help message\n" "\n" "options:\n" - " -w erase userdata and cache\n" - " -s <serial number> specify device serial number\n" + " -w erase userdata and cache (and format\n" + " if supported by partition type)\n" + " -u do not first erase partition before\n" + " formatting\n" + " -s <specific device> specify device serial number\n" + " or path to device port\n" + " -l with \"devices\", lists device paths\n" " -p <product> specify product name\n" " -c <cmdline> override kernel commandline\n" " -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" ); } @@ -257,7 +319,7 @@ void *load_bootable_image(unsigned page_size, const char *kernel, const char *ra kdata = load_file(kernel, &ksize); if(kdata == 0) { - fprintf(stderr, "cannot load '%s'\n", kernel); + fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno)); return 0; } @@ -277,7 +339,7 @@ void *load_bootable_image(unsigned page_size, const char *kernel, const char *ra if(ramdisk) { rdata = load_file(ramdisk, &rsize); if(rdata == 0) { - fprintf(stderr,"cannot load '%s'\n", ramdisk); + fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno)); return 0; } } @@ -424,6 +486,122 @@ 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; +} + +/* Until we get lazy inode table init working in make_ext4fs, we need to + * erase partitions of type ext4 before flashing a filesystem so no stale + * inodes are left lying around. Otherwise, e2fsck gets very upset. + */ +static int needs_erase(const char *part) +{ + /* The function fb_format_supported() currently returns the value + * we want, so just call it. + */ + return fb_format_supported(usb, part); +} + +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': %s\n", fname, strerror(errno)); + fb_queue_flash(pname, data, sz); + } +} + void do_update_signature(zipfile_t zip, char *fn) { void *data; @@ -434,7 +612,7 @@ void do_update_signature(zipfile_t zip, char *fn) fb_queue_command("signature", "installing signature"); } -void do_update(char *fn) +void do_update(char *fn, int erase_first) { void *zdata; unsigned zsize; @@ -447,7 +625,7 @@ void do_update(char *fn) fb_queue_query_save("product", cur_product, sizeof(cur_product)); zdata = load_file(fn, &zsize); - if (zdata == 0) die("failed to load '%s'", fn); + if (zdata == 0) die("failed to load '%s': %s", fn, strerror(errno)); zip = init_zipfile(zdata, zsize); if(zip == 0) die("failed to access zipdata in '%s'"); @@ -472,17 +650,26 @@ void do_update(char *fn) data = unzip_file(zip, "boot.img", &sz); if (data == 0) die("update package missing boot.img"); do_update_signature(zip, "boot.sig"); + if (erase_first && needs_erase("boot")) { + fb_queue_erase("boot"); + } fb_queue_flash("boot", data, sz); data = unzip_file(zip, "recovery.img", &sz); if (data != 0) { do_update_signature(zip, "recovery.sig"); + if (erase_first && needs_erase("recovery")) { + fb_queue_erase("recovery"); + } fb_queue_flash("recovery", data, sz); } data = unzip_file(zip, "system.img", &sz); if (data == 0) die("update package missing system.img"); do_update_signature(zip, "system.sig"); + if (erase_first && needs_erase("system")) { + fb_queue_erase("system"); + } fb_queue_flash("system", data, sz); } @@ -504,7 +691,7 @@ void do_send_signature(char *fn) fb_queue_command("signature", "installing signature"); } -void do_flashall(void) +void do_flashall(int erase_first) { char *fname; void *data; @@ -517,26 +704,35 @@ void do_flashall(void) fname = find_item("info", product); if (fname == 0) die("cannot find android-info.txt"); data = load_file(fname, &sz); - if (data == 0) die("could not load android-info.txt"); + if (data == 0) die("could not load android-info.txt: %s", strerror(errno)); setup_requirements(data, sz); fname = find_item("boot", product); data = load_file(fname, &sz); - if (data == 0) die("could not load boot.img"); + if (data == 0) die("could not load boot.img: %s", strerror(errno)); do_send_signature(fname); + if (erase_first && needs_erase("boot")) { + fb_queue_erase("boot"); + } fb_queue_flash("boot", data, sz); fname = find_item("recovery", product); data = load_file(fname, &sz); if (data != 0) { do_send_signature(fname); + if (erase_first && needs_erase("recovery")) { + fb_queue_erase("recovery"); + } fb_queue_flash("recovery", data, sz); } fname = find_item("system", product); data = load_file(fname, &sz); - if (data == 0) die("could not load system.img"); + if (data == 0) die("could not load system.img: %s", strerror(errno)); do_send_signature(fname); + if (erase_first && needs_erase("system")) { + fb_queue_erase("system"); + } fb_queue_flash("system", data, sz); } @@ -561,82 +757,168 @@ 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; int wants_reboot = 0; int wants_reboot_bootloader = 0; + int erase_first = 1; void *data; unsigned sz; unsigned page_size = 2048; int status; + int c; + int r; - skip(1); - if (argc == 0) { + const struct option longopts = { 0, 0, 0, 0 }; + + serial = getenv("ANDROID_SERIAL"); + + while (1) { + c = getopt_long(argc, argv, "wub:n:s:S:lp:c:i:m:h", &longopts, NULL); + if (c < 0) { + break; + } + + switch (c) { + case 'w': + wants_wipe = 1; + break; + case 'u': + erase_first = 0; + 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 'l': + long_listing = 1; + 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; + + 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")) { + if (argc > 0 && !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); } else if(!strcmp(*argv, "erase")) { require(2); + + if (fb_format_supported(usb, argv[1])) { + fprintf(stderr, "******** Did you mean to fastboot format this partition?\n"); + } + fb_queue_erase(argv[1]); skip(2); + } else if(!strcmp(*argv, "format")) { + require(2); + if (erase_first && needs_erase(argv[1])) { + fb_queue_erase(argv[1]); + } + fb_queue_format(argv[1], 0); + skip(2); } else if(!strcmp(*argv, "signature")) { require(2); data = load_file(argv[1], &sz); - if (data == 0) die("could not load '%s'", argv[1]); + if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno)); if (sz != 256) die("signature must be 256 bytes"); fb_queue_download("signature", data, sz); fb_queue_command("signature", "installing signature"); @@ -678,9 +960,10 @@ 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); + if (erase_first && needs_erase(pname)) { + fb_queue_erase(pname); + } + do_flash(usb, pname, fname); } else if(!strcmp(*argv, "flash:raw")) { char *pname = argv[1]; char *kname = argv[2]; @@ -697,14 +980,14 @@ int main(int argc, char **argv) fb_queue_flash(pname, data, sz); } else if(!strcmp(*argv, "flashall")) { skip(1); - do_flashall(); + do_flashall(erase_first); wants_reboot = 1; } else if(!strcmp(*argv, "update")) { if (argc > 1) { - do_update(argv[1]); + do_update(argv[1], erase_first); skip(2); } else { - do_update("update.zip"); + do_update("update.zip", erase_first); skip(1); } wants_reboot = 1; @@ -718,7 +1001,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(); @@ -726,7 +1011,8 @@ int main(int argc, char **argv) fb_queue_command("reboot-bootloader", "rebooting into bootloader"); } - usb = open_device(); + if (fb_queue_is_empty()) + return 0; status = fb_execute_queue(usb); return (status) ? 1 : 0; diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 9e043fe..c1b2964 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,25 @@ #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 */ -void fb_queue_flash(const char *ptn, void *data, unsigned sz);; +int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...); +int fb_format_supported(usb_handle *usb, const char *partition); +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); @@ -52,6 +59,7 @@ void fb_queue_command(const char *cmd, const char *msg); void fb_queue_download(const char *name, void *data, unsigned size); void fb_queue_notice(const char *notice); int fb_execute_queue(usb_handle *usb); +int fb_queue_is_empty(void); /* util stuff */ void die(const char *fmt, ...); 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..d504ee2 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,20 @@ 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]; + char device_path[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..b7a9ca3 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 @@ -32,6 +32,7 @@ #include <string.h> #include <sys/ioctl.h> +#include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <fcntl.h> @@ -66,7 +67,7 @@ */ #define MAX_USBFS_BULK_SIZE (16 * 1024) -struct usb_handle +struct usb_handle { char fname[64]; int desc; @@ -85,12 +86,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,36 +104,40 @@ 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; + struct stat st; + int result; + 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]; - int result; + // Keep it short enough because some bootloaders are borked if the URB len is > 255 + // 128 is too big by 1. + __u16 buffer[127]; memset(buffer, 0, sizeof(buffer)); @@ -156,29 +161,65 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, } } + /* We need to get a path that represents a particular port on a particular + * hub. We are passed an fd that was obtained by opening an entry under + * /dev/bus/usb. Unfortunately, the names of those entries change each + * time devices are plugged and unplugged. So how to get a repeatable + * path? udevadm provided the inspiration. We can get the major and + * minor of the device file, read the symlink that can be found here: + * /sys/dev/char/<major>:<minor> + * and then use the last element of that path. As a concrete example, I + * have an Android device at /dev/bus/usb/001/027 so working with bash: + * $ ls -l /dev/bus/usb/001/027 + * crw-rw-r-- 1 root plugdev 189, 26 Apr 9 11:03 /dev/bus/usb/001/027 + * $ ls -l /sys/dev/char/189:26 + * lrwxrwxrwx 1 root root 0 Apr 9 11:03 /sys/dev/char/189:26 -> + * ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3 + * So our device_path would be 1-4.2.3 which says my device is connected + * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per + * http://www.linux-usb.org/FAQ.html). + */ + info.device_path[0] = '\0'; + result = fstat(fd, &st); + if (!result && S_ISCHR(st.st_mode)) { + char cdev[128]; + char link[256]; + char *slash; + ssize_t link_len; + snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(cdev, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) + snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1); + } + } + for(i = 0; i < cfg->bNumInterfaces; i++) { if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE)) return -1; 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 +229,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 +247,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 +281,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 +318,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 +333,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 +368,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 +394,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 +418,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..1548ba8 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); @@ -264,6 +264,7 @@ static int try_device(io_service_t device, usb_handle *handle) { SInt32 score; HRESULT result; UInt8 serialIndex; + UInt32 locationId; // Create an intermediate plugin. kr = IOCreatePlugInInterfaceForService(device, @@ -284,7 +285,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. */ @@ -322,6 +323,13 @@ static int try_device(io_service_t device, usb_handle *handle) { goto error; } + kr = (*dev)->GetLocationID(dev, &locationId); + if (kr != 0) { + ERR("GetLocationId"); + goto error; + } + snprintf(handle->info.device_path, sizeof(handle->info.device_path), "usb:%lX", locationId); + kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); if (serialIndex > 0) { @@ -339,7 +347,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 +372,8 @@ static int try_device(io_service_t device, usb_handle *handle) { if (dev != NULL) { (*dev)->Release(dev); } - - return -1; + + return -1; } @@ -399,7 +407,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 +417,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..7aa36b2 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, @@ -311,6 +311,8 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) { info.serial_number[0] = 0; } + info.device_path[0] = 0; + if (callback(&info) == 0) { return 1; } 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..e51c9cf --- /dev/null +++ b/fs_mgr/fs_mgr.c @@ -0,0 +1,638 @@ +/* + * 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 }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, + { "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 *fs_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 (fs_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 (fs_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, char *target) +{ + pid_t pid; + int status; + int ret; + long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID; + char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro"; + + /* Check for the types of filesystems we know how to check */ + if (!strcmp(type, "ext2") || !strcmp(type, "ext3") || !strcmp(type, "ext4")) { + /* + * First try to mount and unmount the filesystem. We do this because + * the kernel is more efficient than e2fsck in running the journal and + * processing orphaned inodes, and on at least one device with a + * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes + * to do what the kernel does in about a second. + * + * After mounting and unmounting the filesystem, run e2fsck, and if an + * error is recorded in the filesystem superblock, e2fsck will do a full + * check. Otherwise, it does nothing. If the kernel cannot mount the + * filesytsem due to an error, e2fsck is still run to do a full check + * fix the filesystem. + */ + ret = mount(blk_dev, target, type, tmpmnt_flags, tmpmnt_opts); + if (! ret) { + umount(target); + } + + 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, fstab[i].mnt_point); + } + + 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(n_blk_dev, WAIT_TIMEOUT); + } + + if (fstab[i].fs_mgr_flags & MF_CHECK) { + check_fs(n_blk_dev, fstab[i].type, fstab[i].mnt_point); + } + + /* 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/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h new file mode 100644 index 0000000..76abb83 --- /dev/null +++ b/fs_mgr/include/fs_mgr.h @@ -0,0 +1,27 @@ +/* + * 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_H +#define __CORE_FS_MGR_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); + +#endif /* __CORE_FS_MGR_H */ + diff --git a/gpttool/Android.mk b/gpttool/Android.mk index a9fffe9..b8f9844 100644 --- a/gpttool/Android.mk +++ b/gpttool/Android.mk @@ -7,7 +7,6 @@ LOCAL_SRC_FILES := gpttool.c LOCAL_STATIC_LIBRARIES := libz LOCAL_MODULE := gpttool -LOCAL_MODULE_TAGS := eng include $(BUILD_HOST_EXECUTABLE) diff --git a/include/arch/darwin-x86/AndroidConfig.h b/include/arch/darwin-x86/AndroidConfig.h deleted file mode 100644 index c8ccc7e..0000000 --- a/include/arch/darwin-x86/AndroidConfig.h +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Android config -- "Darwin". Used for X86 Mac OS X. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ - -/* #define HAVE_FUTEX */ - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -/* #define HAVE_OOM_ADJ */ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_MACOSX_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H - -/* - * Define this if you have <sys/sendfile.h> - */ -/* #define HAVE_SYS_SENDFILE_H 1 */ - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -/* #define HAVE_IOCTL */ - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - */ -/* #define HAVE_POSIX_CLOCKS */ - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#if (defined(__ppc__) || defined(__ppc64__)) -# define HAVE_BIG_ENDIAN -#elif (defined(__i386__) || defined(__x86_64__)) -# define HAVE_LITTLE_ENDIAN -#endif - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -/* #define HAVE_OFF64_T */ - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 0 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - - -/* - * Add any extra platform-specific defines here. - */ -#define _THREAD_SAFE - -/* - * Define if we have <malloc.h> header - */ -/* #define HAVE_MALLOC_H */ - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if we include <sys/mount.h> for statfs() - */ -#define INCLUDE_SYS_MOUNT_FOR_STATFS 1 - -/* - * What CPU architecture does this platform use? - */ -#if (defined(__ppc__) || defined(__ppc64__)) -# define ARCH_PPC -#elif (defined(__i386__) || defined(__x86_64__)) -# define ARCH_X86 -#endif - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.dylib" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE char * - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - * - * For tools apps, we'll treat is as not case sensitive. - */ -/* #define OS_CASE_SENSITIVE */ - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/freebsd-x86/AndroidConfig.h b/include/arch/freebsd-x86/AndroidConfig.h deleted file mode 100644 index d828bd5..0000000 --- a/include/arch/freebsd-x86/AndroidConfig.h +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Android config -- "FreeBSD". Used for desktop x86 FreeBSD. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * make sure we are building for FreeBSD - */ -#ifndef OS_FREEBSD -#define OS_FREEBSD -#endif -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ -/* #define HAVE_FUTEX */ - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -/* #define HAVE_OOM_ADJ */ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_SYSV_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -/* #define HAVE_TERMIO_H */ - -/* - * Define this if you have <sys/sendfile.h> - */ -/* #define HAVE_SYS_SENDFILE_H 1 */ - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - * Desktop Linux has this in librt, but it's broken in goobuntu, yielding - * mildly or wildly inaccurate results. - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Define this if we have linux style epoll() - */ -/* #define HAVE_EPOLL */ - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -/* #define HAVE_ENDIAN_H */ -#define HAVE_LITTLE_ENDIAN - -/* - * Define this if you have sys/endian.h - * NOTE: mutually exclusive with HAVE_ENDIAN_H - */ -#define HAVE_SYS_ENDIAN_H - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -/* #define HAVE_OFF64_T */ - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -/*#define HAVE_INOTIFY 1*/ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -#define HAVE_SYSTEM_PROPERTY_SERVER - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * Define if we include <sys/mount.h> for statfs() - */ -#define INCLUDE_SYS_MOUNT_FOR_STATFS 1 - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -/* #define HAVE_PRCTL 1 */ - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <alloca.h> does not exist - * NOTE: <alloca.h> defines alloca() which - * on FreeBSD is defined in <stdlib.h> - */ -#define HAVE_NO_ALLOCA_H - -/* - * Defines CLOCK_PROCESS_CPUTIME_ID for clock_gettime() - * XXX: CLOCK_PROF seems to be commonly used replacement - */ -#ifndef CLOCK_PROCESS_CPUTIME_ID -#define CLOCK_PROCESS_CPUTIME_ID CLOCK_PROF -#endif - -/* - * Define if <stdint.h> exists. - */ -/* #define HAVE_STDINT_H */ - -/* - * Define if <stdbool.h> exists. - */ -/* #define HAVE_STDBOOL_H */ - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-arm/AndroidConfig.h b/include/arch/linux-arm/AndroidConfig.h deleted file mode 100644 index cae112b..0000000 --- a/include/arch/linux-arm/AndroidConfig.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Android config -- "android-arm". Used for ARM device builds. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have pthread_setname_np()? - * - * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with - * the same name but different parameters, so we can't use that here.) - */ -#define HAVE_ANDROID_PTHREAD_SETNAME_NP - -/* - * Do we have the futex syscall? - */ -#define HAVE_FUTEX - -/* - * Define if we already have the futex wrapper functions defined. Yes if - * compiling against bionic. - */ -#define HAVE_FUTEX_WRAPPERS 1 - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_ANDROID_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -#define HAVE_TIMEDWAIT_MONOTONIC - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -/* #define _FILE_OFFSET_BITS 64 */ -/* #define _LARGEFILE_SOURCE 1 */ - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -#define HAVE_GETTID - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ -#ifndef __linux__ -#define __linux__ -#endif - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we're running on *our* linux on device or emulator. - */ -#define HAVE_ANDROID_OS 1 - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ -#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -#define HAVE_INOTIFY 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -#define HAVE_LIBC_SYSTEM_PROPERTIES 1 - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_ARM - -/* - * Define if the size of enums is as short as possible, - */ -/* #define HAVE_SHORT_ENUMS */ - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * Do we have __memcmp16()? - */ -#define HAVE__MEMCMP16 1 - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-ppc/AndroidConfig.h b/include/arch/linux-ppc/AndroidConfig.h deleted file mode 100644 index 00706dc..0000000 --- a/include/arch/linux-ppc/AndroidConfig.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - * 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. - */ - -/* - * Android config -- "Linux". Used for desktop ppc Linux. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ - -#define HAVE_FUTEX - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_SYSV_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R - -/* - * Define this if we have gethostbyname_r(). - */ -#define HAVE_GETHOSTBYNAME_R - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - * Desktop Linux has this in librt, but it's broken in goobuntu, yielding - * mildly or wildly inaccurate results. - */ -/*#define HAVE_POSIX_CLOCKS*/ - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_BIG_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 1 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_PPC - - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -/*#define HAVE_INOTIFY 1*/ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -#define HAVE_SYSTEM_PROPERTY_SERVER - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -/* #define HAVE_STRLCPY 1 */ - -/* - * Define if the open_memstream() function exists on the system. - */ -#define HAVE_OPEN_MEMSTREAM 1 - -/* - * Define if the BSD funopen() function exists on the system. - */ -/* #define HAVE_FUNOPEN 1 */ - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/linux-sh/AndroidConfig.h b/include/arch/linux-sh/AndroidConfig.h deleted file mode 100644 index 5562eae..0000000 --- a/include/arch/linux-sh/AndroidConfig.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -/* - * Android config -- "android-sh". Used for SuperH device builds. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have pthread_setname_np()? - * - * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with - * the same name but different parameters, so we can't use that here.) - */ -#define HAVE_ANDROID_PTHREAD_SETNAME_NP - -/* - * Do we have the futex syscall? - */ -#define HAVE_FUTEX - -/* - * Define if we already have the futex wrapper functions defined. Yes if - * compiling against bionic. - */ -#define HAVE_FUTEX_WRAPPERS 1 - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_ANDROID_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -/* #define _FILE_OFFSET_BITS 64 */ -/* #define _LARGEFILE_SOURCE 1 */ - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 0 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -#define HAVE_GETTID - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ -/* #define __linux__ */ /* for SuperH */ - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we're running on *our* linux on device or emulator. - */ -#define HAVE_ANDROID_OS 1 - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ -#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -#define HAVE_INOTIFY 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -#define HAVE_LIBC_SYSTEM_PROPERTIES 1 - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_SH - -/* - * Define if the size of enums is as short as possible, - */ -/* #define HAVE_SHORT_ENUMS */ - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * Do we have __memcmp16()? - * - * TODO : Investigate the perfomance impact of __memcmp16() - * and implement it. - * This influences on dalvikVM's string performance. - * See dalvik/vm/InlineNative.c. - */ -/* #define HAVE__MEMCMP16 */ - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * For dalvik/libcore - */ -#define CANT_PASS_VALIST_AS_CHARPTR - -/* - * For external/bluez/utils/tools/hciattach.c - * TODO : This definition should be somewhere in bionic/libc/kernel/(*). - * Cosider the place and move it there. - */ -#define N_TTY 0 - -/* - * Whether or not _Unwind_Context is defined as a struct. - */ -#define HAVE_UNWIND_CONTEXT_STRUCT - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/linux-x86/AndroidConfig.h b/include/arch/linux-x86/AndroidConfig.h deleted file mode 100644 index 7dcaa98..0000000 --- a/include/arch/linux-x86/AndroidConfig.h +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Android config -- "Linux". Used for desktop x86 Linux. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have the futex syscall? - */ - -#define HAVE_FUTEX - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_SYSV_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against MSVCRT.DLL - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS - -/* - * Define this if we have localtime_r(). - */ -#define HAVE_LOCALTIME_R - -/* - * Define this if we have gethostbyname_r(). - */ -#define HAVE_GETHOSTBYNAME_R - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - * Desktop Linux has this in librt, but it's broken in goobuntu, yielding - * mildly or wildly inaccurate results. - */ -/*#define HAVE_POSIX_CLOCKS*/ - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -/* #define HAVE_TIMEDWAIT_MONOTONIC */ - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 1 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 1 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -/* #define HAVE_GETTID */ - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -/*#define HAVE_INOTIFY 1*/ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -#define HAVE_SYSTEM_PROPERTY_SERVER - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -/* #define HAVE_STRLCPY 1 */ - -/* - * Define if the open_memstream() function exists on the system. - */ -#define HAVE_OPEN_MEMSTREAM 1 - -/* - * Define if the BSD funopen() function exists on the system. - */ -/* #define HAVE_FUNOPEN 1 */ - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Define if writev() exists - */ -#define HAVE_WRITEV 1 - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -#endif /*_ANDROID_CONFIG_H*/ diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h deleted file mode 100644 index 05dd220..0000000 --- a/include/arch/target_linux-x86/AndroidConfig.h +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright 2005 The Android Open Source Project - * - * Android config -- "target_linux-x86". Used for x86 linux target devices. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - * -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX - */ -#define HAVE_PTHREADS - -/* - * Do we have pthread_setname_np()? - * - * (HAVE_PTHREAD_SETNAME_NP is used by WebKit to enable a function with - * the same name but different parameters, so we can't use that here.) - */ -#define HAVE_ANDROID_PTHREAD_SETNAME_NP - -/* - * Do we have the futex syscall? - */ -#define HAVE_FUTEX - -/* - * Define if we already have the futex wrapper functions defined. Yes if - * compiling against bionic. - */ -#define HAVE_FUTEX_WRAPPERS 1 - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#define HAVE_FORKEXEC - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -#define HAVE_OOM_ADJ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_ANDROID_IPC 1 - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#define HAVE_POSIX_FILEMAP 1 - -/* - * Define this if you have <termio.h> - */ -#define HAVE_TERMIO_H 1 - -/* - * Define this if you have <sys/sendfile.h> - */ -#define HAVE_SYS_SENDFILE_H 1 - -/* - * Define this if you build against have Microsoft C runtime (MSVCRT.DLL) - */ -/* #define HAVE_MS_C_RUNTIME */ - -/* - * Define this if you have sys/uio.h - */ -#define HAVE_SYS_UIO_H 1 - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -#define HAVE_SYMLINKS 1 - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -#define HAVE_IOCTL - -/* - * Define this if we want to use WinSock. - */ -/* #define HAVE_WINSOCK */ - -/* - * Define this if have clock_gettime() and friends - * - */ -#define HAVE_POSIX_CLOCKS - -/* - * Define this if we have pthread_cond_timedwait_monotonic() and - * clock_gettime(CLOCK_MONOTONIC). - */ -#define HAVE_TIMEDWAIT_MONOTONIC - -/* - * Define this if we have linux style epoll() - */ -#define HAVE_EPOLL - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#define HAVE_ENDIAN_H -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -/* - * #define _FILE_OFFSET_BITS 64 - * #define _LARGEFILE_SOURCE 1 - */ - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 0 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Defined if we have the gettid() system call. - */ -#define HAVE_GETTID - -/* - * Defined if we have the sched_setscheduler() call - */ -#define HAVE_SCHED_SETSCHEDULER - -/* - * Add any extra platform-specific defines here. - */ -#ifndef __linux__ -#define __linux__ -#endif - -/* - * Define if we have <malloc.h> header - */ -#define HAVE_MALLOC_H - -/* - * Define if we're running on *our* linux on device or emulator. - */ -#define HAVE_ANDROID_OS 1 - -/* - * Define if we have Linux-style non-filesystem Unix Domain Sockets - */ -#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1 - -/* - * Define if we have Linux's inotify in <sys/inotify.h>. - */ -#define HAVE_INOTIFY 1 - -/* - * Define if we have madvise() in <sys/mman.h> - */ -#define HAVE_MADVISE 1 - -/* - * Define if we have Linux's dbus - */ -/* #define HAVE_DBUS 1 */ - -/* - * Define if tm struct has tm_gmtoff field - */ -#define HAVE_TM_GMTOFF 1 - -/* - * Define if dirent struct has d_type field - */ -#define HAVE_DIRENT_D_TYPE 1 - -/* - * Define if libc includes Android system properties implementation. - */ -#define HAVE_LIBC_SYSTEM_PROPERTIES 1 - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.so" - -/* - * Do we have __memcmp16()? - */ -/* #define HAVE__MEMCMP16 1 */ - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * Do we have the sigaction flag SA_NOCLDWAIT? - */ -#define HAVE_SA_NOCLDWAIT - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '/' - -/* - * Is the filesystem case sensitive? - */ -#define OS_CASE_SENSITIVE - -/* - * Define if <sys/socket.h> exists. - */ -#define HAVE_SYS_SOCKET_H 1 - -/* - * Define if the strlcpy() function exists on the system. - */ -#define HAVE_STRLCPY 1 - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -#define HAVE_FUNOPEN 1 - -/* - * Define if prctl() exists - */ -#define HAVE_PRCTL 1 - -/* - * Whether or not _Unwind_Context is defined as a struct. - */ -#define HAVE_UNWIND_CONTEXT_STRUCT - -/* - * Define if <stdint.h> exists. - */ -#define HAVE_STDINT_H 1 - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H 1 - -/* - * Define if <sched.h> exists. - */ -#define HAVE_SCHED_H 1 - -/* - * Define if pread() exists - */ -#define HAVE_PREAD 1 - -/* - * Define if we have st_mtim in struct stat - */ -#define HAVE_STAT_ST_MTIM 1 - -/* - * Define if printf() supports %zd for size_t arguments - */ -#define HAVE_PRINTF_ZD 1 - -#endif /* _ANDROID_CONFIG_H */ diff --git a/include/arch/windows/AndroidConfig.h b/include/arch/windows/AndroidConfig.h deleted file mode 100644 index ad890b4..0000000 --- a/include/arch/windows/AndroidConfig.h +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Android config -- "CYGWIN_NT-5.1". - * - * Cygwin has pthreads, but GDB seems to get confused if you use it to - * create threads. By "confused", I mean it freezes up the first time the - * debugged process creates a thread, even if you use CreateThread. The - * mere presence of pthreads linkage seems to cause problems. - */ -#ifndef _ANDROID_CONFIG_H -#define _ANDROID_CONFIG_H - -/* - * =========================================================================== - * !!! IMPORTANT !!! - * =========================================================================== - * - * This file is included by ALL C/C++ source files. Don't put anything in - * here unless you are absolutely certain it can't go anywhere else. - * - * Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//" - * comments. - */ - -/* - * Threading model. Choose one: - * - * HAVE_PTHREADS - use the pthreads library. - * HAVE_WIN32_THREADS - use Win32 thread primitives. - */ -#define HAVE_WIN32_THREADS - -/* - * Do we have the futex syscall? - */ - -/* #define HAVE_FUTEX */ - - -/* - * Process creation model. Choose one: - * - * HAVE_FORKEXEC - use fork() and exec() - * HAVE_WIN32_PROC - use CreateProcess() - */ -#ifdef __CYGWIN__ -# define HAVE_FORKEXEC -#else -# define HAVE_WIN32_PROC -#endif - -/* - * Process out-of-memory adjustment. Set if running on Linux, - * where we can write to /proc/<pid>/oom_adj to modify the out-of-memory - * badness adjustment. - */ -/* #define HAVE_OOM_ADJ */ - -/* - * IPC model. Choose one: - * - * HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget). - * HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap). - * HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping). - * HAVE_ANDROID_IPC - use Android versions (?, mmap). - */ -#define HAVE_WIN32_IPC - -/* - * Memory-mapping model. Choose one: - * - * HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h - * HAVE_WIN32_FILEMAP - use Win32 filemaps - */ -#ifdef __CYGWIN__ -#define HAVE_POSIX_FILEMAP -#else -#define HAVE_WIN32_FILEMAP -#endif - -/* - * Define this if you have <termio.h> - */ -#ifdef __CYGWIN__ -# define HAVE_TERMIO_H -#endif - -/* - * Define this if you have <sys/sendfile.h> - */ -#ifdef __CYGWIN__ -# define HAVE_SYS_SENDFILE_H 1 -#endif - -/* - * Define this if you build against MSVCRT.DLL - */ -#ifndef __CYGWIN__ -# define HAVE_MS_C_RUNTIME -#endif - -/* - * Define this if you have sys/uio.h - */ -#ifdef __CYGWIN__ -#define HAVE_SYS_UIO_H -#endif - - -/* - * Define this if we have localtime_r(). - */ -/* #define HAVE_LOCALTIME_R */ - -/* - * Define this if we have gethostbyname_r(). - */ -/* #define HAVE_GETHOSTBYNAME_R */ - -/* - * Define this if we have ioctl(). - */ -/* #define HAVE_IOCTL */ - -/* - * Define this if we want to use WinSock. - */ -#ifndef __CYGWIN__ -#define HAVE_WINSOCK -#endif - -/* - * Define this if your platforms implements symbolic links - * in its filesystems - */ -/* #define HAVE_SYMLINKS */ - -/* - * Define this if have clock_gettime() and friends - */ -/* #define HAVE_POSIX_CLOCKS */ - -/* - * Endianness of the target machine. Choose one: - * - * HAVE_ENDIAN_H -- have endian.h header we can include. - * HAVE_LITTLE_ENDIAN -- we are little endian. - * HAVE_BIG_ENDIAN -- we are big endian. - */ -#ifdef __CYGWIN__ -#define HAVE_ENDIAN_H -#endif - -#define HAVE_LITTLE_ENDIAN - -/* - * We need to choose between 32-bit and 64-bit off_t. All of our code should - * agree on the same size. For desktop systems, use 64-bit values, - * because some of our libraries (e.g. wxWidgets) expect to be built that way. - */ -#define _FILE_OFFSET_BITS 64 -#define _LARGEFILE_SOURCE 1 - -/* - * Define if platform has off64_t (and lseek64 and other xxx64 functions) - */ -#define HAVE_OFF64_T - -/* - * Defined if we have the backtrace() call for retrieving a stack trace. - * Needed for CallStack to operate; if not defined, CallStack is - * non-functional. - */ -#define HAVE_BACKTRACE 0 - -/* - * Defined if we have the dladdr() call for retrieving the symbol associated - * with a memory address. If not defined, stack crawls will not have symbolic - * information. - */ -#define HAVE_DLADDR 0 - -/* - * Defined if we have the cxxabi.h header for demangling C++ symbols. If - * not defined, stack crawls will be displayed with raw mangled symbols - */ -#define HAVE_CXXABI 0 - -/* - * Define if tm struct has tm_gmtoff field - */ -/* #define HAVE_TM_GMTOFF 1 */ - -/* - * Define if dirent struct has d_type field - */ -/* #define HAVE_DIRENT_D_TYPE 1 */ - -/* - * Define if libc includes Android system properties implementation. - */ -/* #define HAVE_LIBC_SYSTEM_PROPERTIES */ - -/* - * Define if system provides a system property server (should be - * mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES). - */ -/* #define HAVE_SYSTEM_PROPERTY_SERVER */ - -/* - * Define if we have madvise() in <sys/mman.h> - */ -/*#define HAVE_MADVISE 1*/ - -/* - * Add any extra platform-specific defines here. - */ -#define WIN32 1 /* stock Cygwin doesn't define these */ -#define _WIN32 1 -#define _WIN32_WINNT 0x0500 /* admit to using >= Win2K */ - -#define HAVE_WINDOWS_PATHS /* needed by simulator */ - -/* - * What CPU architecture does this platform use? - */ -#define ARCH_X86 - -/* - * sprintf() format string for shared library naming. - */ -#define OS_SHARED_LIB_FORMAT_STR "lib%s.dll" - -/* - * type for the third argument to mincore(). - */ -#define MINCORE_POINTER_TYPE unsigned char * - -/* - * The default path separator for the platform - */ -#define OS_PATH_SEPARATOR '\\' - -/* - * Is the filesystem case sensitive? - */ -/* #define OS_CASE_SENSITIVE */ - -/* - * Define if <sys/socket.h> exists. - * Cygwin has it, but not MinGW. - */ -#ifdef USE_MINGW -/* #define HAVE_SYS_SOCKET_H */ -#else -#define HAVE_SYS_SOCKET_H 1 -#endif - -/* - * Define if the strlcpy() function exists on the system. - */ -/* #define HAVE_STRLCPY 1 */ - -/* - * Define if the open_memstream() function exists on the system. - */ -/* #define HAVE_OPEN_MEMSTREAM 1 */ - -/* - * Define if the BSD funopen() function exists on the system. - */ -/* #define HAVE_FUNOPEN 1 */ - -/* - * Define if <winsock2.h> exists. - * Only MinGW has it. - */ -#ifdef USE_MINGW -#define HAVE_WINSOCK2_H 1 -#else -/* #define HAVE_WINSOCK2_H */ -#endif - -/* - * Various definitions missing in MinGW - */ -#ifdef USE_MINGW -#define S_IRGRP 0 -#endif - -/* - * Define if writev() exists. - */ -/* #define HAVE_WRITEV */ - -/* - * Define if <stdint.h> exists. - */ -/* #define HAVE_STDINT_H */ - -/* - * Define if <stdbool.h> exists. - */ -#define HAVE_STDBOOL_H - -/* - * Define if <sched.h> exists. - */ -/* #define HAVE_SCHED_H */ - -/* - * Define if pread() exists - */ -/* #define HAVE_PREAD 1 */ - -/* - * Define if we have st_mtim in struct stat - */ -/* #define HAVE_STAT_ST_MTIM 1 */ - -/* - * Define if printf() supports %zd for size_t arguments - */ -/* #define HAVE_PRINTF_ZD 1 */ - -#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/WifiScanner.h b/include/corkscrew/demangle.h index 92822e9..04b0225 100644 --- a/nexus/WifiScanner.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,34 +14,29 @@ * limitations under the License. */ -#ifndef _WIFISCANNER_H -#define _WIFISCANNER_H +/* C++ symbol name demangling. */ -#include <pthread.h> +#ifndef _CORKSCREW_DEMANGLE_H +#define _CORKSCREW_DEMANGLE_H -class Supplicant; +#include <sys/types.h> +#include <stdbool.h> -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); +#ifdef __cplusplus +extern "C" { +#endif - void run(); -}; +/* + * 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..14bfad6 --- /dev/null +++ b/include/corkscrew/map_info.h @@ -0,0 +1,74 @@ +/* + * 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> +#include <stdint.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_writable; + 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 a readable map. */ +bool is_readable_map(const map_info_t* milist, uintptr_t addr); +/* Returns true if the addr is in a writable map. */ +bool is_writable_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); + +/* Flushes the cached memory map so the next call to + * acquire_my_map_info_list() gets fresh data. */ +void flush_my_map_info_list(); + +#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..76276d8 --- /dev/null +++ b/include/corkscrew/ptrace.h @@ -0,0 +1,134 @@ +/* + * 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> +#include <stdint.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 + +#if __mips__ +/* ptrace() GET_REGS context. */ +typedef struct pt_regs_mips { + uint64_t regs[32]; + uint64_t lo; + uint64_t hi; + uint64_t cp0_epc; + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; +} pt_regs_mips_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..4998750 --- /dev/null +++ b/include/corkscrew/symbol_table.h @@ -0,0 +1,59 @@ +/* + * 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 <stdint.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/atomic-arm.h b/include/cutils/atomic-arm.h index 16fe512..172a0cd 100644 --- a/include/cutils/atomic-arm.h +++ b/include/cutils/atomic-arm.h @@ -18,91 +18,75 @@ #define ANDROID_CUTILS_ATOMIC_ARM_H #include <stdint.h> -#include <machine/cpu-features.h> -extern inline void android_compiler_barrier(void) +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier() { __asm__ __volatile__ ("" : : : "memory"); } -#if ANDROID_SMP == 0 -extern inline void android_memory_barrier(void) -{ - android_compiler_barrier(); -} -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier() { +#if ANDROID_SMP == 0 android_compiler_barrier(); -} -#elif defined(__ARM_HAVE_DMB) -extern inline void android_memory_barrier(void) -{ +#else __asm__ __volatile__ ("dmb" : : : "memory"); +#endif } -extern inline void android_memory_store_barrier(void) -{ - __asm__ __volatile__ ("dmb st" : : : "memory"); -} -#elif defined(__ARM_HAVE_LDREX_STREX) -extern inline void android_memory_barrier(void) -{ - __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory"); -} -extern inline void android_memory_store_barrier(void) + +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier() { - android_memory_barrier(); -} +#if ANDROID_SMP == 0 + android_compiler_barrier(); #else -extern inline void android_memory_barrier(void) -{ - typedef void (kuser_memory_barrier)(void); - (*(kuser_memory_barrier *)0xffff0fa0)(); -} -extern inline void android_memory_store_barrier(void) -{ - android_memory_barrier(); -} + __asm__ __volatile__ ("dmb st" : : : "memory"); #endif +} -extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_acquire_load(volatile const int32_t *ptr) { int32_t value = *ptr; android_memory_barrier(); return value; } -extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_release_load(volatile const int32_t *ptr) { android_memory_barrier(); return *ptr; } -extern inline void android_atomic_acquire_store(int32_t value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) { *ptr = value; android_memory_barrier(); } -extern inline void android_atomic_release_store(int32_t value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store(int32_t value, volatile int32_t *ptr) { android_memory_barrier(); *ptr = value; } -#if defined(__thumb__) -extern int android_atomic_cas(int32_t old_value, int32_t new_value, - volatile int32_t *ptr); -#elif defined(__ARM_HAVE_LDREX_STREX) -extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) { int32_t prev, status; do { __asm__ __volatile__ ("ldrex %0, [%3]\n" "mov %1, #0\n" "teq %0, %4\n" +#ifdef __thumb2__ + "it eq\n" +#endif "strexeq %1, %5, [%3]" : "=&r" (prev), "=&r" (status), "+m"(*ptr) : "r" (ptr), "Ir" (old_value), "r" (new_value) @@ -110,47 +94,26 @@ extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, } while (__builtin_expect(status != 0, 0)); return prev != old_value; } -#else -extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, - volatile int32_t *ptr) -{ - typedef int (kuser_cmpxchg)(int32_t, int32_t, volatile int32_t *); - int32_t prev, status; - prev = *ptr; - do { - status = (*(kuser_cmpxchg *)0xffff0fc0)(old_value, new_value, ptr); - if (__builtin_expect(status == 0, 1)) - return 0; - prev = *ptr; - } while (prev == old_value); - return 1; -} -#endif -extern inline int android_atomic_acquire_cas(int32_t old_value, - int32_t new_value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int android_atomic_acquire_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) { int status = android_atomic_cas(old_value, new_value, ptr); android_memory_barrier(); return status; } -extern inline int android_atomic_release_cas(int32_t old_value, - int32_t new_value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int android_atomic_release_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) { android_memory_barrier(); return android_atomic_cas(old_value, new_value, ptr); } - -#if defined(__thumb__) -extern int32_t android_atomic_add(int32_t increment, - volatile int32_t *ptr); -#elif defined(__ARM_HAVE_LDREX_STREX) -extern inline int32_t android_atomic_add(int32_t increment, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) { int32_t prev, tmp, status; android_memory_barrier(); @@ -165,34 +128,19 @@ extern inline int32_t android_atomic_add(int32_t increment, } while (__builtin_expect(status != 0, 0)); return prev; } -#else -extern inline int32_t android_atomic_add(int32_t increment, - volatile int32_t *ptr) -{ - int32_t prev, status; - android_memory_barrier(); - do { - prev = *ptr; - status = android_atomic_cas(prev, prev + increment, ptr); - } while (__builtin_expect(status != 0, 0)); - return prev; -} -#endif -extern inline int32_t android_atomic_inc(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t android_atomic_inc(volatile int32_t *addr) { return android_atomic_add(1, addr); } -extern inline int32_t android_atomic_dec(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t android_atomic_dec(volatile int32_t *addr) { return android_atomic_add(-1, addr); } -#if defined(__thumb__) -extern int32_t android_atomic_and(int32_t value, volatile int32_t *ptr); -#elif defined(__ARM_HAVE_LDREX_STREX) -extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) { int32_t prev, tmp, status; android_memory_barrier(); @@ -207,23 +155,9 @@ extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) } while (__builtin_expect(status != 0, 0)); return prev; } -#else -extern inline int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) -{ - int32_t prev, status; - android_memory_barrier(); - do { - prev = *ptr; - status = android_atomic_cas(prev, prev & value, ptr); - } while (__builtin_expect(status != 0, 0)); - return prev; -} -#endif -#if defined(__thumb__) -extern int32_t android_atomic_or(int32_t value, volatile int32_t *ptr); -#elif defined(__ARM_HAVE_LDREX_STREX) -extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) { int32_t prev, tmp, status; android_memory_barrier(); @@ -238,17 +172,5 @@ extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) } while (__builtin_expect(status != 0, 0)); return prev; } -#else -extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) -{ - int32_t prev, status; - android_memory_barrier(); - do { - prev = *ptr; - status = android_atomic_cas(prev, prev | value, ptr); - } while (__builtin_expect(status != 0, 0)); - return prev; -} -#endif #endif /* ANDROID_CUTILS_ATOMIC_ARM_H */ diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h index 49f3e70..0b13138 100644 --- a/include/cutils/atomic-inline.h +++ b/include/cutils/atomic-inline.h @@ -47,8 +47,8 @@ extern "C" { #include <cutils/atomic-arm.h> #elif defined(__i386__) || defined(__x86_64__) #include <cutils/atomic-x86.h> -#elif defined(__sh__) -/* implementation is in atomic-android-sh.c */ +#elif defined(__mips__) +#include <cutils/atomic-mips.h> #else #error atomic operations are unsupported #endif diff --git a/include/cutils/atomic-mips.h b/include/cutils/atomic-mips.h new file mode 100644 index 0000000..f9d3e25 --- /dev/null +++ b/include/cutils/atomic-mips.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_CUTILS_ATOMIC_MIPS_H +#define ANDROID_CUTILS_ATOMIC_MIPS_H + +#include <stdint.h> + +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + __asm__ __volatile__ ("sync" : : : "memory"); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + __asm__ __volatile__ ("sync" : : : "memory"); +} +#endif + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE void +android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE void +android_atomic_release_store(int32_t value, volatile int32_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " li %[status], 1\n" + " bne %[prev], %[old], 9f\n" + " move %[status], %[new_value]\n" + " sc %[status], (%[ptr])\n" + "9:\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [old] "r" (old_value), [new_value] "r" (new_value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev != old_value; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + int status = android_atomic_cas(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas(old_value, new_value, ptr); +} + + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_swap(int32_t new_value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ( + " move %[status], %[new_value]\n" + " ll %[prev], (%[ptr])\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [new_value] "r" (new_value) + ); + } while (__builtin_expect(status == 0, 0)); + android_memory_barrier(); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_add(int32_t increment, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " addu %[status], %[prev], %[inc]\n" + " sc %[status], (%[ptr])\n" + : [status] "=&r" (status), [prev] "=&r" (prev) + : [ptr] "r" (ptr), [inc] "Ir" (increment) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " and %[status], %[prev], %[value]\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [value] "Ir" (value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " or %[status], %[prev], %[value]\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [value] "Ir" (value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_MIPS_H */ diff --git a/include/cutils/atomic-x86.h b/include/cutils/atomic-x86.h index 438012e..9480f57 100644 --- a/include/cutils/atomic-x86.h +++ b/include/cutils/atomic-x86.h @@ -19,60 +19,66 @@ #include <stdint.h> -extern inline void android_compiler_barrier(void) +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void) { __asm__ __volatile__ ("" : : : "memory"); } #if ANDROID_SMP == 0 -extern inline void android_memory_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) { android_compiler_barrier(); } -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) { android_compiler_barrier(); } #else -extern inline void android_memory_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) { __asm__ __volatile__ ("mfence" : : : "memory"); } -extern inline void android_memory_store_barrier(void) +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) { android_compiler_barrier(); } #endif -extern inline int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_acquire_load(volatile const int32_t *ptr) { int32_t value = *ptr; android_compiler_barrier(); return value; } -extern inline int32_t android_atomic_release_load(volatile const int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_release_load(volatile const int32_t *ptr) { android_memory_barrier(); return *ptr; } -extern inline void android_atomic_acquire_store(int32_t value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE void +android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) { *ptr = value; android_memory_barrier(); } -extern inline void android_atomic_release_store(int32_t value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE void +android_atomic_release_store(int32_t value, volatile int32_t *ptr) { android_compiler_barrier(); *ptr = value; } -extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int +android_atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t *ptr) { int32_t prev; __asm__ __volatile__ ("lock; cmpxchgl %1, %2" @@ -82,24 +88,26 @@ extern inline int android_atomic_cas(int32_t old_value, int32_t new_value, return prev != old_value; } -extern inline int android_atomic_acquire_cas(int32_t old_value, - int32_t new_value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int +android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) { /* Loads are not reordered with other loads. */ return android_atomic_cas(old_value, new_value, ptr); } -extern inline int android_atomic_release_cas(int32_t old_value, - int32_t new_value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int +android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) { /* Stores are not reordered with other stores. */ return android_atomic_cas(old_value, new_value, ptr); } -extern inline int32_t android_atomic_add(int32_t increment, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_add(int32_t increment, volatile int32_t *ptr) { __asm__ __volatile__ ("lock; xaddl %0, %1" : "+r" (increment), "+m" (*ptr) @@ -108,18 +116,20 @@ extern inline int32_t android_atomic_add(int32_t increment, return increment; } -extern inline int32_t android_atomic_inc(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_inc(volatile int32_t *addr) { return android_atomic_add(1, addr); } -extern inline int32_t android_atomic_dec(volatile int32_t *addr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_dec(volatile int32_t *addr) { return android_atomic_add(-1, addr); } -extern inline int32_t android_atomic_and(int32_t value, - volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_and(int32_t value, volatile int32_t *ptr) { int32_t prev, status; do { @@ -129,7 +139,8 @@ extern inline int32_t android_atomic_and(int32_t value, return prev; } -extern inline int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_or(int32_t value, volatile int32_t *ptr) { int32_t prev, status; do { diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h new file mode 100644 index 0000000..4eda523 --- /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; + +typedef struct { + debugger_action_t action; + pid_t tid; + uintptr_t abort_msg_address; +} 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/fs.h b/include/cutils/fs.h new file mode 100644 index 0000000..fd5296b --- /dev/null +++ b/include/cutils/fs.h @@ -0,0 +1,62 @@ +/* + * 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_FS_H +#define __CUTILS_FS_H + +#include <sys/types.h> + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Ensure that directory exists with given mode and owners. + */ +extern int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid); + +/* + * Read single plaintext integer from given file, correctly handling files + * partially written with fs_write_atomic_int(). + */ +extern int fs_read_atomic_int(const char* path, int* value); + +/* + * Write single plaintext integer to given file, creating backup while + * in progress. + */ +extern int fs_write_atomic_int(const char* path, int value); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_FS_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..8b045c7 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 @@ -339,7 +279,88 @@ extern "C" { : (void)0 ) #endif - +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose radio log message using the current LOG_TAG. + */ +#ifndef RLOGV +#if LOG_NDEBUG +#define RLOGV(...) ((void)0) +#else +#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef RLOGV_IF +#if LOG_NDEBUG +#define RLOGV_IF(cond, ...) ((void)0) +#else +#define RLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug radio log message using the current LOG_TAG. + */ +#ifndef RLOGD +#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGD_IF +#define RLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info radio log message using the current LOG_TAG. + */ +#ifndef RLOGI +#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGI_IF +#define RLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning radio log message using the current LOG_TAG. + */ +#ifndef RLOGW +#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGW_IF +#define RLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error radio log message using the current LOG_TAG. + */ +#ifndef RLOGE +#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGE_IF +#define RLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + // --------------------------------------------------------------------- @@ -392,10 +413,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 +428,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 +452,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/mspace.h b/include/cutils/mspace.h deleted file mode 100644 index 93fe48e..0000000 --- a/include/cutils/mspace.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 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. - */ - -/* A wrapper file for dlmalloc.h that defines prototypes for the - * mspace_*() functions, which provide an interface for creating - * multiple heaps. - */ - -#ifndef MSPACE_H_ -#define MSPACE_H_ - -/* It's a pain getting the mallinfo stuff to work - * with Linux, OSX, and klibc, so just turn it off - * for now. - * TODO: make mallinfo work - */ -#define NO_MALLINFO 1 - -/* Allow setting the maximum heap footprint. - */ -#define USE_MAX_ALLOWED_FOOTPRINT 1 - -#define USE_CONTIGUOUS_MSPACES 1 -#if USE_CONTIGUOUS_MSPACES -#define HAVE_MMAP 0 -#define HAVE_MORECORE 1 -#define MORECORE_CONTIGUOUS 0 -#endif - -#define MSPACES 1 -#define ONLY_MSPACES 1 -#include "../../../../bionic/libc/bionic/dlmalloc.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - mspace_usable_size(void* p); - - Returns the number of bytes you can actually use in - an allocated chunk, which may be more than you requested (although - often not) due to alignment and minimum size constraints. - You can use this many bytes without worrying about - overwriting other allocated objects. This is not a particularly great - programming practice. mspace_usable_size can be more useful in - debugging and assertions, for example: - - p = mspace_malloc(msp, n); - assert(mspace_usable_size(msp, p) >= 256); -*/ -size_t mspace_usable_size(mspace, const void*); - -#if USE_CONTIGUOUS_MSPACES -/* - Similar to create_mspace(), but the underlying memory is - guaranteed to be contiguous. No more than max_capacity - bytes is ever allocated to the mspace. - */ -mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity, - int locked); - -/* - Identical to create_contiguous_mspace, but labels the mapping 'mspace/name' - instead of 'mspace' -*/ -mspace create_contiguous_mspace_with_name(size_t starting_capacity, - size_t max_capacity, int locked, const char *name); - -/* - Identical to create_contiguous_mspace, but uses previously mapped memory. -*/ -mspace create_contiguous_mspace_with_base(size_t starting_capacity, - size_t max_capacity, int locked, void *base); - -size_t destroy_contiguous_mspace(mspace msp); - -/* - Returns the position of the "break" within the given mspace. -*/ -void *contiguous_mspace_sbrk0(mspace msp); -#endif - -/* - Call the handler for each block in the specified mspace. - chunkptr and chunklen refer to the heap-level chunk including - the chunk overhead, and userptr and userlen refer to the - user-usable part of the chunk. If the chunk is free, userptr - will be NULL and userlen will be 0. userlen is not guaranteed - to be the same value passed into malloc() for a given chunk; - it is >= the requested size. - */ -void mspace_walk_heap(mspace msp, - void(*handler)(const void *chunkptr, size_t chunklen, - const void *userptr, size_t userlen, void *arg), void *harg); - -/* - mspace_walk_free_pages(handler, harg) - - Calls the provided handler on each free region in the specified - mspace. The memory between start and end are guaranteed not to - contain any important data, so the handler is free to alter the - contents in any way. This can be used to advise the OS that large - free regions may be swapped out. - - The value in harg will be passed to each call of the handler. - */ -void mspace_walk_free_pages(mspace msp, - void(*handler)(void *start, void *end, void *arg), void *harg); - -#ifdef __cplusplus -}; /* end of extern "C" */ -#endif - -#endif /* MSPACE_H_ */ diff --git a/nexus/OpenVpnController.h b/include/cutils/multiuser.h index 529aab5..635ddb1 100644 --- a/nexus/OpenVpnController.h +++ b/include/cutils/multiuser.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,29 +14,28 @@ * limitations under the License. */ -#ifndef _OPEN_VPN_CONTROLLER_H -#define _OPEN_VPN_CONTROLLER_H +#ifndef __CUTILS_MULTIUSER_H +#define __CUTILS_MULTIUSER_H -#include "PropertyManager.h" -#include "VpnController.h" +#include <sys/types.h> -class ServiceManager; -class IControllerHandler; +#ifdef __cplusplus +extern "C" { +#endif -class OpenVpnController : public VpnController { -private: - ServiceManager *mServiceManager; +// NOTE: keep in sync with android.os.UserId -public: - OpenVpnController(PropertyManager *propmngr, IControllerHandler *handlers); - virtual ~OpenVpnController(); +#define MULTIUSER_APP_PER_USER_RANGE 100000 - int start(); - int stop(); +typedef uid_t userid_t; +typedef uid_t appid_t; -private: - int enable(); - int disable(); -}; +extern userid_t multiuser_get_user_id(uid_t uid); +extern appid_t multiuser_get_app_id(uid_t uid); +extern uid_t multiuser_get_uid(userid_t userId, appid_t appId); +#ifdef __cplusplus +} #endif + +#endif /* __CUTILS_MULTIUSER_H */ 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..dbdbd60 100644 --- a/include/cutils/tztime.h +++ b/include/cutils/tztime.h @@ -17,39 +17,8 @@ #ifndef _CUTILS_TZTIME_H #define _CUTILS_TZTIME_H -#include <time.h> - -#ifdef __cplusplus -extern "C" { -#endif - -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 */ - -struct strftime_locale { - const char *mon[12]; /* short names */ - const char *month[12]; /* long names */ - const char *standalone_month[12]; /* long standalone names */ - const char *wday[7]; /* short names */ - const char *weekday[7]; /* long names */ - const char *X_fmt; - const char *x_fmt; - const char *c_fmt; - const char *am; - const char *pm; - const char *date_fmt; -}; - -size_t strftime_tz(char *s, size_t max, const char *format, const struct tm *tm, const struct strftime_locale *locale); - -#endif /* !HAVE_ANDROID_OS */ - -#ifdef __cplusplus -} -#endif +// TODO: fix both callers to just include <bionic_time.h> themselves. +#include <bionic_time.h> #endif /* __CUTILS_TZTIME_H */ 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/diskconfig/diskconfig.h b/include/diskconfig/diskconfig.h index d4f468c..d45b99e 100644 --- a/include/diskconfig/diskconfig.h +++ b/include/diskconfig/diskconfig.h @@ -19,6 +19,7 @@ #define __LIBS_DISKCONFIG_H #include <stdint.h> +#include <sys/types.h> #ifdef __cplusplus extern "C" { diff --git a/include/ion/ion.h b/include/ion/ion.h new file mode 100644 index 0000000..018c0a1 --- /dev/null +++ b/include/ion/ion.h @@ -0,0 +1,43 @@ +/* + * 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 heap_mask, + unsigned int flags, struct ion_handle **handle); +int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, int *handle_fd); +int ion_sync_fd(int fd, int handle_fd); +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/mincrypt/rsa.h b/include/mincrypt/rsa.h index 7d7d571..d7429fc 100644 --- a/include/mincrypt/rsa.h +++ b/include/mincrypt/rsa.h @@ -13,14 +13,14 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -42,6 +42,7 @@ typedef struct RSAPublicKey { uint32_t n0inv; /* -1 / n[0] mod 2^32 */ uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ + int exponent; /* 3 or 65537 */ } RSAPublicKey; int RSA_verify(const RSAPublicKey *key, 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/netutils/ifc.h b/include/netutils/ifc.h index 67a4a45..1f5421d 100644 --- a/include/netutils/ifc.h +++ b/include/netutils/ifc.h @@ -34,6 +34,9 @@ extern int ifc_down(const char *name); extern int ifc_enable(const char *ifname); extern int ifc_disable(const char *ifname); +#define RESET_IPV4_ADDRESSES 0x01 +#define RESET_IPV6_ADDRESSES 0x02 +#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) extern int ifc_reset_connections(const char *ifname, const int reset_mask); extern int ifc_get_addr(const char *name, in_addr_t *addr); @@ -66,6 +69,8 @@ extern int ifc_configure(const char *ifname, in_addr_t address, uint32_t prefixLength, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2); +extern in_addr_t prefixLengthToIpv4Netmask(int prefix_length); + __END_DECLS #endif /* _NETUTILS_IFC_H_ */ diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h new file mode 100644 index 0000000..0505cda --- /dev/null +++ b/include/private/android_filesystem_capability.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013 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. + */ + +/* + * Taken from linux/capability.h, with minor modifications + */ + +#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H +#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H + +#include <stdint.h> + +#define __user +#define __u32 uint32_t +#define __le32 uint32_t + +#define _LINUX_CAPABILITY_VERSION_1 0x19980330 +#define _LINUX_CAPABILITY_U32S_1 1 +#define _LINUX_CAPABILITY_VERSION_2 0x20071026 +#define _LINUX_CAPABILITY_U32S_2 2 +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 +#define _LINUX_CAPABILITY_U32S_3 2 + +typedef struct __user_cap_header_struct { + __u32 version; + int pid; +} __user *cap_user_header_t; + +typedef struct __user_cap_data_struct { + __u32 effective; + __u32 permitted; + __u32 inheritable; +} __user *cap_user_data_t; + +#define VFS_CAP_REVISION_MASK 0xFF000000 +#define VFS_CAP_REVISION_SHIFT 24 +#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK +#define VFS_CAP_FLAGS_EFFECTIVE 0x000001 +#define VFS_CAP_REVISION_1 0x01000000 +#define VFS_CAP_U32_1 1 +#define XATTR_CAPS_SZ_1 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1)) +#define VFS_CAP_REVISION_2 0x02000000 +#define VFS_CAP_U32_2 2 +#define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2)) +#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2 +#define VFS_CAP_U32 VFS_CAP_U32_2 +#define VFS_CAP_REVISION VFS_CAP_REVISION_2 + +struct vfs_cap_data { + __le32 magic_etc; + struct { + __le32 permitted; + __le32 inheritable; + } data[VFS_CAP_U32]; +}; + +#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1 +#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1 +#define CAP_CHOWN 0 +#define CAP_DAC_OVERRIDE 1 +#define CAP_DAC_READ_SEARCH 2 +#define CAP_FOWNER 3 +#define CAP_FSETID 4 +#define CAP_KILL 5 +#define CAP_SETGID 6 +#define CAP_SETUID 7 +#define CAP_SETPCAP 8 +#define CAP_LINUX_IMMUTABLE 9 +#define CAP_NET_BIND_SERVICE 10 +#define CAP_NET_BROADCAST 11 +#define CAP_NET_ADMIN 12 +#define CAP_NET_RAW 13 +#define CAP_IPC_LOCK 14 +#define CAP_IPC_OWNER 15 +#define CAP_SYS_MODULE 16 +#define CAP_SYS_RAWIO 17 +#define CAP_SYS_CHROOT 18 +#define CAP_SYS_PTRACE 19 +#define CAP_SYS_PACCT 20 +#define CAP_SYS_ADMIN 21 +#define CAP_SYS_BOOT 22 +#define CAP_SYS_NICE 23 +#define CAP_SYS_RESOURCE 24 +#define CAP_SYS_TIME 25 +#define CAP_SYS_TTY_CONFIG 26 +#define CAP_MKNOD 27 +#define CAP_LEASE 28 +#define CAP_AUDIT_WRITE 29 +#define CAP_AUDIT_CONTROL 30 +#define CAP_SETFCAP 31 +#define CAP_MAC_OVERRIDE 32 +#define CAP_MAC_ADMIN 33 +#define CAP_SYSLOG 34 +#define CAP_WAKE_ALARM 35 +#define CAP_LAST_CAP CAP_WAKE_ALARM +#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) +#define CAP_TO_INDEX(x) ((x) >> 5) +#define CAP_TO_MASK(x) (1 << ((x) & 31)) + +#undef __user +#undef __u32 +#undef __le32 + +#endif diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index ce1e55d..53bd166 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -25,6 +25,13 @@ #include <string.h> #include <sys/stat.h> #include <sys/types.h> +#include <stdint.h> + +#ifdef HAVE_ANDROID_OS +#include <linux/capability.h> +#else +#include "android_filesystem_capability.h" +#endif /* This is the master Users and Groups config for the platform. ** DO NOT EVER RENUMBER. @@ -53,7 +60,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 +68,9 @@ #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_CLAT 1029 /* clat part of nat464 */ +#define AID_LOOP_RADIO 1030 /* loop radio devices */ #define AID_SHELL 2000 /* adb and debug shell user */ #define AID_CACHE 2001 /* cache access */ @@ -75,11 +85,20 @@ #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ +#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */ #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 */ + +#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ +#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */ #if !defined(EXCLUDE_FS_CONFIG_STRUCTURES) struct android_id_info { @@ -105,7 +124,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 +132,8 @@ static const struct android_id_info android_ids[] = { { "diag", AID_DIAG, }, { "net_bt_admin", AID_NET_BT_ADMIN, }, { "net_bt", AID_NET_BT, }, + { "net_bt_stack", AID_NET_BT_STACK, }, + { "sdcard_r", AID_SDCARD_R, }, { "sdcard_rw", AID_SDCARD_RW, }, { "media_rw", AID_MEDIA_RW, }, { "vpn", AID_VPN, }, @@ -125,17 +146,20 @@ static const struct android_id_info android_ids[] = { { "net_admin", AID_NET_ADMIN, }, { "net_bw_stats", AID_NET_BW_STATS, }, { "net_bw_acct", AID_NET_BW_ACCT, }, + { "loop_radio", AID_LOOP_RADIO, }, { "misc", AID_MISC, }, { "nobody", AID_NOBODY, }, + { "clat", AID_CLAT, }, }; #define android_id_count \ (sizeof(android_ids) / sizeof(android_ids[0])) - + struct fs_path_config { unsigned mode; unsigned uid; unsigned gid; + uint64_t capabilities; const char *prefix; }; @@ -145,26 +169,26 @@ struct fs_path_config { ** way up to the root. */ -static struct fs_path_config android_dirs[] = { - { 00770, AID_SYSTEM, AID_CACHE, "cache" }, - { 00771, AID_SYSTEM, AID_SYSTEM, "data/app" }, - { 00771, AID_SYSTEM, AID_SYSTEM, "data/app-private" }, - { 00771, AID_SYSTEM, AID_SYSTEM, "data/dalvik-cache" }, - { 00771, AID_SYSTEM, AID_SYSTEM, "data/data" }, - { 00771, AID_SHELL, AID_SHELL, "data/local/tmp" }, - { 00771, AID_SHELL, AID_SHELL, "data/local" }, - { 01771, AID_SYSTEM, AID_MISC, "data/misc" }, - { 00770, AID_DHCP, AID_DHCP, "data/misc/dhcp" }, - { 00775, AID_MEDIA_RW, AID_MEDIA_RW, "data/media" }, - { 00775, AID_MEDIA_RW, AID_MEDIA_RW, "data/media/Music" }, - { 00771, AID_SYSTEM, AID_SYSTEM, "data" }, - { 00750, AID_ROOT, AID_SHELL, "sbin" }, - { 00755, AID_ROOT, AID_SHELL, "system/bin" }, - { 00755, AID_ROOT, AID_SHELL, "system/vendor" }, - { 00755, AID_ROOT, AID_SHELL, "system/xbin" }, - { 00755, AID_ROOT, AID_ROOT, "system/etc/ppp" }, - { 00777, AID_ROOT, AID_ROOT, "sdcard" }, - { 00755, AID_ROOT, AID_ROOT, 0 }, +static const struct fs_path_config android_dirs[] = { + { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/dalvik-cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local" }, + { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" }, + { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" }, + { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" }, + { 00755, AID_ROOT, AID_ROOT, 0, 0 }, }; /* Rules for files. @@ -173,59 +197,62 @@ static struct fs_path_config android_dirs[] = { ** way up to the root. Prefixes ending in * denotes wildcard ** and will allow partial matches. */ -static struct fs_path_config android_files[] = { - { 00440, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.rc" }, - { 00550, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.sh" }, - { 00440, AID_ROOT, AID_SHELL, "system/etc/init.trout.rc" }, - { 00550, AID_ROOT, AID_SHELL, "system/etc/init.ril" }, - { 00550, AID_ROOT, AID_SHELL, "system/etc/init.testmenu" }, - { 00550, AID_DHCP, AID_SHELL, "system/etc/dhcpcd/dhcpcd-run-hooks" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/main.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/input.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/audio.conf" }, - { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluetooth/network.conf" }, - { 00444, AID_NET_BT, AID_NET_BT, "system/etc/bluetooth/blacklist.conf" }, - { 00640, AID_SYSTEM, AID_SYSTEM, "system/etc/bluetooth/auto_pairing.conf" }, - { 00444, AID_RADIO, AID_AUDIO, "system/etc/AudioPara4.csv" }, - { 00555, AID_ROOT, AID_ROOT, "system/etc/ppp/*" }, - { 00555, AID_ROOT, AID_ROOT, "system/etc/rc.*" }, - { 00644, AID_SYSTEM, AID_SYSTEM, "data/app/*" }, - { 00644, AID_MEDIA_RW, AID_MEDIA_RW, "data/media/*" }, - { 00644, AID_SYSTEM, AID_SYSTEM, "data/app-private/*" }, - { 00644, AID_APP, AID_APP, "data/data/*" }, - /* the following two files are INTENTIONALLY set-gid and not set-uid. - * Do not change. */ - { 02755, AID_ROOT, AID_NET_RAW, "system/bin/ping" }, - { 02750, AID_ROOT, AID_INET, "system/bin/netcfg" }, - /* the following five files are INTENTIONALLY set-uid, but they - * are NOT included on user builds. */ - { 06755, AID_ROOT, AID_ROOT, "system/xbin/su" }, - { 06755, AID_ROOT, AID_ROOT, "system/xbin/librank" }, - { 06755, AID_ROOT, AID_ROOT, "system/xbin/procrank" }, - { 06755, AID_ROOT, AID_ROOT, "system/xbin/procmem" }, - { 06755, AID_ROOT, AID_ROOT, "system/xbin/tcpdump" }, - { 04770, AID_ROOT, AID_RADIO, "system/bin/pppd-ril" }, - /* the following file is INTENTIONALLY set-uid, and IS included - * in user builds. */ - { 06750, AID_ROOT, AID_SHELL, "system/bin/run-as" }, - { 00755, AID_ROOT, AID_SHELL, "system/bin/*" }, - { 00755, AID_ROOT, AID_ROOT, "system/lib/valgrind/*" }, - { 00755, AID_ROOT, AID_SHELL, "system/xbin/*" }, - { 00755, AID_ROOT, AID_SHELL, "system/vendor/bin/*" }, - { 00750, AID_ROOT, AID_SHELL, "sbin/*" }, - { 00755, AID_ROOT, AID_ROOT, "bin/*" }, - { 00750, AID_ROOT, AID_SHELL, "init*" }, - { 00750, AID_ROOT, AID_SHELL, "charger*" }, - { 00644, AID_ROOT, AID_ROOT, 0 }, +static const struct fs_path_config android_files[] = { + { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" }, + { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.trout.rc" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.testmenu" }, + { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" }, + { 00440, AID_BLUETOOTH, AID_BLUETOOTH, 0, "system/etc/dbus.conf" }, + { 00444, AID_RADIO, AID_AUDIO, 0, "system/etc/AudioPara4.csv" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" }, + { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app/*" }, + { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" }, + { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" }, + { 00644, AID_APP, AID_APP, 0, "data/data/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/bin/ping" }, + + /* the following file is INTENTIONALLY set-gid and not set-uid. + * Do not change. */ + { 02750, AID_ROOT, AID_INET, 0, "system/bin/netcfg" }, + + /* the following five files are INTENTIONALLY set-uid, but they + * are NOT included on user builds. */ + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/su" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/tcpdump" }, + { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" }, + + /* the following file has enhanced capabilities and IS included in user builds. */ + { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" }, + + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "init*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "charger*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" }, + { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, + { 00644, AID_ROOT, AID_ROOT, 0, 0 }, }; static inline void fs_config(const char *path, int dir, - unsigned *uid, unsigned *gid, unsigned *mode) + unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities) { - struct fs_path_config *pc; + const struct fs_path_config *pc; int plen; - + + if (path[0] == '/') { + path++; + } + pc = dir ? android_dirs : android_files; plen = strlen(path); for(; pc->prefix; pc++){ @@ -245,9 +272,10 @@ static inline void fs_config(const char *path, int dir, *uid = pc->uid; *gid = pc->gid; *mode = (*mode & (~07777)) | pc->mode; - + *capabilities = pc->capabilities; + #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/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h index 2d7fdcf..4864d5a 100644 --- a/include/private/pixelflinger/ggl_context.h +++ b/include/private/pixelflinger/ggl_context.h @@ -42,10 +42,30 @@ inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) { #else inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) { +#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2 + uint32_t r; + __asm__("wsbh %0, %1;" + "rotr %0, %0, 16" + : "=r" (r) + : "r" (v) + ); + return r; +#else return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +#endif } inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) { +#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2 + uint32_t r; + __asm__("wsbh %0, %1;" + "rotr %0, %0, 16" + : "=r" (r) + : "r" (v) + ); + return r; +#else return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +#endif } #endif diff --git a/include/private/pixelflinger/ggl_fixed.h b/include/private/pixelflinger/ggl_fixed.h index 96fdb32..217ec04 100644 --- a/include/private/pixelflinger/ggl_fixed.h +++ b/include/private/pixelflinger/ggl_fixed.h @@ -190,6 +190,272 @@ inline int64_t gglMulii(int32_t x, int32_t y) ); return res; } +#elif defined(__mips__) + +/*inline MIPS implementations*/ +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST; +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) { + GGLfixed result,tmp,tmp1,tmp2; + + if (__builtin_constant_p(shift)) { + if (shift == 0) { + asm ("mult %[a], %[b] \t\n" + "mflo %[res] \t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp) + : [a]"r"(a),[b]"r"(b) + : "%hi","%lo" + ); + } else if (shift == 32) + { + asm ("mult %[a], %[b] \t\n" + "li %[tmp],1\t\n" + "sll %[tmp],%[tmp],0x1f\t\n" + "mflo %[res] \t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp]\t\n" /*obit*/ + "sra %[tmp],%[tmp],0x1f \t\n" + "mfhi %[res] \t\n" + "addu %[res],%[res],%[tmp]\t\n" + "addu %[res],%[res],%[tmp1]\t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1) + : [a]"r"(a),[b]"r"(b),[shift]"I"(shift) + : "%hi","%lo" + ); + } else if ((shift >0) && (shift < 32)) + { + asm ("mult %[a], %[b] \t\n" + "li %[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[shiftm1] \t\n" + "mflo %[res] \t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/ + "addu %[res],%[res],%[tmp] \t\n" + "mfhi %[tmp] \t\n" + "addu %[tmp],%[tmp],%[tmp1] \t\n" + "sll %[tmp],%[tmp],%[lshift] \t\n" + "srl %[res],%[res],%[rshift] \t\n" + "or %[res],%[res],%[tmp] \t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[lshift]"I"(32-shift),[rshift]"I"(shift),[shiftm1]"I"(shift-1) + : "%hi","%lo" + ); + } else { + asm ("mult %[a], %[b] \t\n" + "li %[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[shiftm1] \t\n" + "mflo %[res] \t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/ + "sra %[tmp2],%[tmp],0x1f \t\n" + "addu %[res],%[res],%[tmp] \t\n" + "mfhi %[tmp] \t\n" + "addu %[tmp],%[tmp],%[tmp2] \t\n" + "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/ + "srl %[tmp2],%[res],%[rshift] \t\n" + "srav %[res], %[tmp],%[rshift]\t\n" + "sll %[tmp],%[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[norbits] \t\n" + "or %[tmp],%[tmp],%[tmp2] \t\n" + "movz %[res],%[tmp],%[bit5] \t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[norbits]"I"(~(shift)),[rshift]"I"(shift),[shiftm1] "I"(shift-1),[bit5]"I"(shift & 0x20) + : "%hi","%lo" + ); + } + } else { + asm ("mult %[a], %[b] \t\n" + "li %[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[shiftm1] \t\n" + "mflo %[res] \t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/ + "sra %[tmp2],%[tmp],0x1f \t\n" + "addu %[res],%[res],%[tmp] \t\n" + "mfhi %[tmp] \t\n" + "addu %[tmp],%[tmp],%[tmp2] \t\n" + "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/ + "srl %[tmp2],%[res],%[rshift] \t\n" + "srav %[res], %[tmp],%[rshift]\t\n" + "sll %[tmp],%[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[norbits] \t\n" + "or %[tmp],%[tmp],%[tmp2] \t\n" + "movz %[res],%[tmp],%[bit5] \t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[norbits]"r"(~(shift)),[rshift] "r"(shift),[shiftm1]"r"(shift-1),[bit5] "r"(shift & 0x20) + : "%hi","%lo" + ); + } + + return result; +} + +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST; +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) { + GGLfixed result,t,tmp1,tmp2; + + if (__builtin_constant_p(shift)) { + if (shift == 0) { + asm ("mult %[a], %[b] \t\n" + "mflo %[lo] \t\n" + "addu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + : "%hi","%lo" + ); + } else if (shift == 32) { + asm ("mult %[a], %[b] \t\n" + "mfhi %[lo] \t\n" + "addu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + : "%hi","%lo" + ); + } else if ((shift>0) && (shift<32)) { + asm ("mult %[a], %[b] \t\n" + "mflo %[res] \t\n" + "mfhi %[t] \t\n" + "srl %[res],%[res],%[rshift] \t\n" + "sll %[t],%[t],%[lshift] \t\n" + "or %[res],%[res],%[t] \t\n" + "addu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift) + : "%hi","%lo" + ); + } else { + asm ("mult %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "mflo %[res] \t\n" + "mfhi %[t] \t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "movz %[res],%[tmp1],%[tmp2]\t\n" + "addu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift) + : "%hi","%lo" + ); + } + } else { + asm ("mult %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "mflo %[res] \t\n" + "mfhi %[t] \t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "movz %[res],%[tmp1],%[tmp2]\t\n" + "addu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift) + : "%hi","%lo" + ); + } + return result; +} + +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST; +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) { + GGLfixed result,t,tmp1,tmp2; + + if (__builtin_constant_p(shift)) { + if (shift == 0) { + asm ("mult %[a], %[b] \t\n" + "mflo %[lo] \t\n" + "subu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + : "%hi","%lo" + ); + } else if (shift == 32) { + asm ("mult %[a], %[b] \t\n" + "mfhi %[lo] \t\n" + "subu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + : "%hi","%lo" + ); + } else if ((shift>0) && (shift<32)) { + asm ("mult %[a], %[b] \t\n" + "mflo %[res] \t\n" + "mfhi %[t] \t\n" + "srl %[res],%[res],%[rshift] \t\n" + "sll %[t],%[t],%[lshift] \t\n" + "or %[res],%[res],%[t] \t\n" + "subu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift) + : "%hi","%lo" + ); + } else { + asm ("mult %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "mflo %[res] \t\n" + "mfhi %[t] \t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "movz %[res],%[tmp1],%[tmp2]\t\n" + "subu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift) + : "%hi","%lo" + ); + } + } else { + asm ("mult %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "mflo %[res] \t\n" + "mfhi %[t] \t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "movz %[res],%[tmp1],%[tmp2]\t\n" + "subu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift) + : "%hi","%lo" + ); + } + return result; +} + +inline int64_t gglMulii(int32_t x, int32_t y) CONST; +inline int64_t gglMulii(int32_t x, int32_t y) { + union { + struct { +#if defined(__MIPSEL__) + int32_t lo; + int32_t hi; +#elif defined(__MIPSEB__) + int32_t hi; + int32_t lo; +#endif + } s; + int64_t res; + }u; + asm("mult %2, %3 \t\n" + "mfhi %1 \t\n" + "mflo %0 \t\n" + : "=r"(u.s.lo), "=&r"(u.s.hi) + : "%r"(x), "r"(y) + : "%hi","%lo" + ); + return u.res; +} #else // ---------------------------------------------------------------------- @@ -232,7 +498,7 @@ inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c) { inline int32_t gglClz(int32_t x) CONST; inline int32_t gglClz(int32_t x) { -#if defined(__arm__) && !defined(__thumb__) +#if (defined(__arm__) && !defined(__thumb__)) || defined(__mips__) return __builtin_clz(x); #else if (!x) return 32; diff --git a/include/sync/sync.h b/include/sync/sync.h new file mode 100644 index 0000000..918acf6 --- /dev/null +++ b/include/sync/sync.h @@ -0,0 +1,62 @@ +/* + * 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 <sys/cdefs.h> +#include <stdint.h> + +__BEGIN_DECLS + +// XXX: These structs are copied from the header "linux/sync.h". +struct sync_fence_info_data { + uint32_t len; + char name[32]; + int32_t status; + uint8_t pt_info[0]; +}; + +struct sync_pt_info { + uint32_t len; + char obj_name[32]; + char driver_name[32]; + int32_t status; + uint64_t timestamp_ns; + uint8_t driver_data[0]; +}; + +/* timeout in msecs */ +int sync_wait(int fd, 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..d246070 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -63,7 +63,10 @@ typedef enum { AUDIO_SOURCE_CAMCORDER = 5, AUDIO_SOURCE_VOICE_RECOGNITION = 6, AUDIO_SOURCE_VOICE_COMMUNICATION = 7, - + AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */ + /* An example of remote presentation is Wifi Display */ + /* where a dongle attached to a TV can be used to */ + /* play the mix captured by this audio source. */ AUDIO_SOURCE_CNT, AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, } audio_source_t; @@ -152,7 +155,7 @@ typedef enum { AUDIO_FORMAT_PCM_SUB_8_24_BIT), } audio_format_t; -typedef enum { +enum { /* output channels */ AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, @@ -250,7 +253,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, @@ -273,7 +278,11 @@ typedef enum { AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, } audio_in_acoustics_t; -typedef enum { +enum { + AUDIO_DEVICE_NONE = 0x0, + /* reserved bits */ + AUDIO_DEVICE_BIT_IN = 0x80000000, + AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, /* output devices */ AUDIO_DEVICE_OUT_EARPIECE = 0x1, AUDIO_DEVICE_OUT_SPEAKER = 0x2, @@ -288,7 +297,10 @@ 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_DEFAULT = 0x8000, + AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, + AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, + AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, + AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET | @@ -302,6 +314,9 @@ 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_REMOTE_SUBMIX | AUDIO_DEVICE_OUT_DEFAULT), AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | @@ -309,17 +324,24 @@ 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, - AUDIO_DEVICE_IN_AMBIENT = 0x20000, - AUDIO_DEVICE_IN_BUILTIN_MIC = 0x40000, - AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000, - AUDIO_DEVICE_IN_WIRED_HEADSET = 0x100000, - AUDIO_DEVICE_IN_AUX_DIGITAL = 0x200000, - AUDIO_DEVICE_IN_VOICE_CALL = 0x400000, - AUDIO_DEVICE_IN_BACK_MIC = 0x800000, - AUDIO_DEVICE_IN_DEFAULT = 0x80000000, + AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, + AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, + AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, + AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, + AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, + AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, + AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, + AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, + AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, + AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, + AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | AUDIO_DEVICE_IN_AMBIENT | @@ -329,13 +351,44 @@ typedef enum { AUDIO_DEVICE_IN_AUX_DIGITAL | AUDIO_DEVICE_IN_VOICE_CALL | AUDIO_DEVICE_IN_BACK_MIC | + AUDIO_DEVICE_IN_REMOTE_SUBMIX | + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | + AUDIO_DEVICE_IN_USB_ACCESSORY | + AUDIO_DEVICE_IN_USB_DEVICE | AUDIO_DEVICE_IN_DEFAULT), AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, -} audio_devices_t; +}; + +typedef uint32_t 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)) + if (((device & AUDIO_DEVICE_BIT_IN) == 0) && + (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) return true; else return false; @@ -343,12 +396,20 @@ static inline bool audio_is_output_device(audio_devices_t device) static inline bool audio_is_input_device(audio_devices_t device) { - if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) - return true; - else - return false; + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) + return true; + } + return false; } +static inline bool audio_is_output_devices(audio_devices_t device) +{ + return (device & AUDIO_DEVICE_BIT_IN) == 0; +} + + static inline bool audio_is_a2dp_device(audio_devices_t device) { if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) @@ -359,6 +420,7 @@ static inline bool audio_is_a2dp_device(audio_devices_t device) static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) { + device &= ~AUDIO_DEVICE_BIT_IN; if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO | AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) return true; @@ -366,6 +428,22 @@ 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_remote_submix_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) + return true; + else + return false; +} + static inline bool audio_is_input_channel(uint32_t channel) { if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) @@ -382,7 +460,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 +526,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 1e0af7d..a6554de 100644 --- a/include/system/audio_policy.h +++ b/include/system/audio_policy.h @@ -30,14 +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; - /* device categories used for audio_policy->set_force_use() */ typedef enum { AUDIO_POLICY_FORCE_NONE, @@ -50,6 +42,8 @@ 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_SYSTEM_ENFORCED, AUDIO_POLICY_FORCE_CFG_CNT, AUDIO_POLICY_FORCE_CFG_MAX = AUDIO_POLICY_FORCE_CFG_CNT - 1, @@ -63,6 +57,7 @@ typedef enum { AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_FOR_RECORD, AUDIO_POLICY_FORCE_FOR_DOCK, + AUDIO_POLICY_FORCE_FOR_SYSTEM, AUDIO_POLICY_FORCE_USE_CNT, AUDIO_POLICY_FORCE_USE_MAX = AUDIO_POLICY_FORCE_USE_CNT - 1, diff --git a/include/system/camera.h b/include/system/camera.h index cdfa256..7a4dd53 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,47 @@ 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, + + /** + * Configure the number of video buffers used for recording. The intended + * video buffer count for recording is passed as arg1, which must be + * greater than 0. This command must be sent before recording is started. + * This command returns INVALID_OPERATION error if it is sent after video + * recording is started, or the command is not supported at all. This + * command also returns a BAD_VALUE error if the intended video buffer + * count is non-positive or too big to be realized. + */ + CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10, }; /** 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 729e92c..82b5fcc 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,58 @@ enum { */ HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar + /* + * 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, + /* + * Android binary blob graphics buffer format: + * + * This format is used to carry task-specific data which does not have a + * standard image structure. The details of the format are left to the two + * endpoints. + * + * A typical use case is for transporting JPEG-compressed images from the + * Camera HAL to the framework or to applications. + * + * Buffers of this format must have a height of 1, and width equal to their + * size in bytes. + */ + HAL_PIXEL_FORMAT_BLOB = 0x21, + + /* + * Android format indicating that the choice of format is entirely up to the + * device-specific Gralloc implementation. + * + * The Gralloc implementation should examine the usage bits passed in when + * allocating a buffer with this format, and it should derive the pixel + * format from those usage flags. This format will never be used with any + * of the GRALLOC_USAGE_SW_* usage flags. + * + * If a buffer of this format is to be used as an OpenGL ES texture, the + * framework will assume that sampling the texture will always return an + * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values). + * + */ + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22, /* Legacy formats (deprecated), used by ImageFormat.java */ HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 diff --git a/include/system/window.h b/include/system/window.h index 1cc4a0a..4698fb3 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -17,10 +17,15 @@ #ifndef SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H #define SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H +#include <cutils/native_handle.h> +#include <errno.h> +#include <limits.h> #include <stdint.h> +#include <string.h> +#include <sync/sync.h> #include <sys/cdefs.h> #include <system/graphics.h> -#include <cutils/native_handle.h> +#include <unistd.h> __BEGIN_DECLS @@ -37,6 +42,14 @@ __BEGIN_DECLS // --------------------------------------------------------------------------- +// This #define may be used to conditionally compile device-specific code to +// support either the prior ANativeWindow interface, which did not pass libsync +// fences around, or the new interface that does. This #define is only present +// when the ANativeWindow interface does include libsync support. +#define ANDROID_NATIVE_WINDOW_HAS_SYNC 1 + +// --------------------------------------------------------------------------- + typedef const native_handle_t* buffer_handle_t; // --------------------------------------------------------------------------- @@ -156,9 +169,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, @@ -211,25 +225,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 */ @@ -275,6 +306,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 */ @@ -351,8 +391,12 @@ struct ANativeWindow * allowed if a specific buffer count has been set. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new dequeueBuffer function that + * outputs a fence file descriptor should be used in its place. */ - int (*dequeueBuffer)(struct ANativeWindow* window, + int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer); /* @@ -361,9 +405,14 @@ struct ANativeWindow * dequeueBuffer first. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but it is essentially a no-op, and calls + * to it should be removed. */ - int (*lockBuffer)(struct ANativeWindow* window, + int (*lockBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); + /* * Hook called by EGL when modifications to the render buffer are done. * This unlocks and post the buffer. @@ -377,8 +426,13 @@ struct ANativeWindow * Buffers MUST be queued in the same order than they were dequeued. * * Returns 0 on success or -errno on error. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new queueBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). */ - int (*queueBuffer)(struct ANativeWindow* window, + int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); /* @@ -404,18 +458,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) * */ @@ -433,12 +489,86 @@ struct ANativeWindow * reference if they might use the buffer after queueing or canceling it. * Holding a reference to a buffer after queueing or canceling it is only * allowed if a specific buffer count has been set. + * + * XXX: This function is deprecated. It will continue to work for some + * time for binary compatibility, but the new cancelBuffer function that + * takes a fence file descriptor should be used in its place (pass a value + * of -1 for the fence file descriptor if there is no valid one to pass). */ - int (*cancelBuffer)(struct ANativeWindow* window, + int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer); + /* + * Hook called by EGL to acquire a buffer. This call may block if no + * buffers are available. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The libsync fence file descriptor returned in the int pointed to by the + * fenceFd argument will refer to the fence that must signal before the + * dequeued buffer may be written to. A value of -1 indicates that the + * caller may access the buffer immediately without waiting on a fence. If + * a valid file descriptor is returned (i.e. any value except -1) then the + * caller is responsible for closing the file descriptor. + * + * Returns 0 on success or -errno on error. + */ + int (*dequeueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer** buffer, int* fenceFd); - void* reserved_proc[2]; + /* + * Hook called by EGL when modifications to the render buffer are done. + * This unlocks and post the buffer. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file descriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. The + * caller must not use the file descriptor after it is passed to + * queueBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*queueBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); + + /* + * Hook used to cancel a buffer that has been dequeued. + * No synchronization is performed between dequeue() and cancel(), so + * either external synchronization is needed, or these functions must be + * called from the same thread. + * + * The window holds a reference to the buffer between dequeueBuffer and + * either queueBuffer or cancelBuffer, so clients only need their own + * reference if they might use the buffer after queueing or canceling it. + * Holding a reference to a buffer after queueing or canceling it is only + * allowed if a specific buffer count has been set. + * + * The fenceFd argument specifies a libsync fence file decsriptor for a + * fence that must signal before the buffer can be accessed. If the buffer + * can be accessed immediately then a value of -1 should be used. + * + * Note that if the client has not waited on the fence that was returned + * from dequeueBuffer, that same fence should be passed to cancelBuffer to + * ensure that future uses of the buffer are preceded by a wait on that + * fence. The caller must not use the file descriptor after it is passed + * to cancelBuffer, and the ANativeWindow implementation is responsible for + * closing it. + * + * Returns 0 on success or -errno on error. + */ + int (*cancelBuffer)(struct ANativeWindow* window, + struct ANativeWindowBuffer* buffer, int fenceFd); }; /* Backwards compatibility: use ANativeWindow (struct ANativeWindow in C). @@ -479,14 +609,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, @@ -496,6 +628,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 deprecated 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. */ @@ -526,7 +693,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. * @@ -545,6 +712,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. * @@ -601,7 +793,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. @@ -626,6 +817,17 @@ static inline int native_window_api_disconnect( return window->perform(window, NATIVE_WINDOW_API_DISCONNECT, api); } +/* + * native_window_dequeue_buffer_and_wait(...) + * Dequeue a buffer and wait on the fence associated with that buffer. The + * buffer may safely be accessed immediately upon this function returning. An + * error is returned if either of the dequeue or the wait operations fail. + */ +static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw, + struct ANativeWindowBuffer** anb) { + return anw->dequeueBuffer_DEPRECATED(anw, anb); +} + __END_DECLS 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..f1a4b43 100644 --- a/include/sysutils/FrameworkListener.h +++ b/include/sysutils/FrameworkListener.h @@ -23,12 +23,19 @@ class SocketClient; class FrameworkListener : public SocketListener { public: - static const int CMD_ARGS_MAX = 16; + static const int CMD_ARGS_MAX = 26; + + /* 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 +44,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/include/usbhost/usbhost.h b/include/usbhost/usbhost.h index 9a6b59c..1d67c12 100644 --- a/include/usbhost/usbhost.h +++ b/include/usbhost/usbhost.h @@ -72,6 +72,19 @@ struct usb_host_context *usb_host_init(void); /* Call this to cleanup the USB host library. */ void usb_host_cleanup(struct usb_host_context *context); +/* Call this to get the inotify file descriptor. */ +int usb_host_get_fd(struct usb_host_context *context); + +/* Call this to initialize the usb host context. */ +int usb_host_load(struct usb_host_context *context, + usb_device_added_cb added_cb, + usb_device_removed_cb removed_cb, + usb_discovery_done_cb discovery_done_cb, + void *client_data); + +/* Call this to read and handle occuring usb event. */ +int usb_host_read_event(struct usb_host_context *context); + /* Call this to monitor the USB bus for new and removed devices. * This is intended to be called from a dedicated thread, * as it will not return until one of the callbacks returns true. diff --git a/init/Android.mk b/init/Android.mk index e9fd884..00d2144 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -15,31 +15,37 @@ LOCAL_SRC_FILES:= \ signal_handler.c \ init_parser.c \ ueventd.c \ - ueventd_parser.c + ueventd_parser.c \ + watchdogd.c ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c LOCAL_CFLAGS += -DBOOTCHART=1 endif +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1 +endif + LOCAL_MODULE:= init LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc - -ifeq ($(HAVE_SELINUX),true) -LOCAL_STATIC_LIBRARIES += libselinux -LOCAL_C_INCLUDES := external/libselinux/include -LOCAL_CFLAGS += -DHAVE_SELINUX -endif +LOCAL_STATIC_LIBRARIES := \ + libfs_mgr \ + libcutils \ + libc \ + libselinux include $(BUILD_EXECUTABLE) -# Make a symlink from /sbin/ueventd to /init -SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd +# Make a symlink from /sbin/ueventd and /sbin/watchdogd to /init +SYMLINKS := \ + $(TARGET_ROOT_OUT)/sbin/ueventd \ + $(TARGET_ROOT_OUT)/sbin/watchdogd + $(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE) $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> ../$(INIT_BINARY)" diff --git a/init/builtins.c b/init/builtins.c index ad73e90..07bd6d3 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -29,14 +29,14 @@ #include <stdlib.h> #include <sys/mount.h> #include <sys/resource.h> +#include <sys/wait.h> #include <linux/loop.h> #include <cutils/partition_utils.h> #include <sys/system_properties.h> +#include <fs_mgr.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#endif #include "init.h" #include "keywords.h" @@ -75,6 +75,63 @@ static int write_file(const char *path, const char *value) } } +static int _open(const char *path) +{ + int fd; + + fd = open(path, O_RDONLY | O_NOFOLLOW); + if (fd < 0) + fd = open(path, O_WRONLY | O_NOFOLLOW); + + return fd; +} + +static int _chown(const char *path, unsigned int uid, unsigned int gid) +{ + int fd; + int ret; + + fd = _open(path); + if (fd < 0) { + return -1; + } + + ret = fchown(fd, uid, gid); + if (ret < 0) { + int errno_copy = errno; + close(fd); + errno = errno_copy; + return -1; + } + + close(fd); + + return 0; +} + +static int _chmod(const char *path, mode_t mode) +{ + int fd; + int ret; + + fd = _open(path); + if (fd < 0) { + return -1; + } + + ret = fchmod(fd, mode); + if (ret < 0) { + int errno_copy = errno; + close(fd); + errno = errno_copy; + return -1; + } + + close(fd); + + return 0; +} + static int insmod(const char *filename, char *options) { void *module; @@ -243,10 +300,10 @@ int do_mkdir(int nargs, char **args) mode = strtoul(args[2], 0, 8); } - ret = mkdir(args[1], mode); + ret = make_dir(args[1], mode); /* chmod in case the directory already exists */ if (ret == -1 && errno == EEXIST) { - ret = chmod(args[1], mode); + ret = _chmod(args[1], mode); } if (ret == -1) { return -errno; @@ -260,9 +317,17 @@ int do_mkdir(int nargs, char **args) gid = decode_uid(args[4]); } - if (chown(args[1], uid, gid)) { + if (_chown(args[1], uid, gid) < 0) { return -errno; } + + /* chown may have cleared S_ISUID and S_ISGID, chmod again */ + if (mode & (S_ISUID | S_ISGID)) { + ret = _chmod(args[1], mode); + if (ret == -1) { + return -errno; + } + } } return 0; @@ -273,12 +338,19 @@ static struct { unsigned flag; } mount_flags[] = { { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, { "nosuid", MS_NOSUID }, { "nodev", MS_NODEV }, { "nodiratime", MS_NODIRATIME }, { "ro", MS_RDONLY }, { "rw", 0 }, { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, { "defaults", 0 }, { 0, 0 }, }; @@ -375,91 +447,86 @@ int do_mount(int nargs, char **args) if (wait) wait_for_file(source, COMMAND_RETRY_TIMEOUT); if (mount(source, target, system, flags, options) < 0) { - /* If this fails, it may be an encrypted filesystem - * or it could just be wiped. If wiped, that will be - * handled later in the boot process. - * We only support encrypting /data. Check - * if we're trying to mount it, and if so, - * assume it's encrypted, mount a tmpfs instead. - * Then save the orig mount parms in properties - * for vold to query when it mounts the real - * encrypted /data. - */ - if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) { - const char *tmpfs_options; - - tmpfs_options = property_get("ro.crypto.tmpfs_options"); - - if (mount("tmpfs", target, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV, - tmpfs_options) < 0) { - return -1; - } - - /* Set the property that triggers the framework to do a minimal - * startup and ask the user for a password - */ - property_set("ro.crypto.state", "encrypted"); - property_set("vold.decrypt", "1"); - } else { - return -1; - } + return -1; } - if (!strcmp(target, DATA_MNT_POINT)) { - char fs_flags[32]; - - /* Save the original mount options */ - property_set("ro.crypto.fs_type", system); - property_set("ro.crypto.fs_real_blkdev", source); - property_set("ro.crypto.fs_mnt_point", target); - if (options) { - property_set("ro.crypto.fs_options", options); - } - snprintf(fs_flags, sizeof(fs_flags), "0x%8.8x", flags); - property_set("ro.crypto.fs_flags", fs_flags); - } } exit_success: - /* If not running encrypted, then set the property saying we are - * unencrypted, and also trigger the action for a nonencrypted system. - */ - if (!strcmp(target, DATA_MNT_POINT)) { - const char *prop; + return 0; - prop = property_get("ro.crypto.state"); - if (! prop) { - prop = "notset"; +} + +int do_mount_all(int nargs, char **args) +{ + pid_t pid; + int ret = -1; + int child_ret = -1; + int status; + const char *prop; + + if (nargs != 2) { + return -1; + } + + /* + * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and + * do the call in the child to provide protection to the main init + * process if anything goes wrong (crash or memory leak), and wait for + * the child to finish in the parent. + */ + pid = fork(); + if (pid > 0) { + /* Parent. Wait for the child to return */ + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + } else { + ret = -1; } - if (strcmp(prop, "encrypted")) { - property_set("ro.crypto.state", "unencrypted"); - action_for_each_trigger("nonencrypted", action_add_queue_tail); + } else if (pid == 0) { + /* child, call fs_mgr_mount_all() */ + klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */ + child_ret = fs_mgr_mount_all(args[1]); + if (child_ret == -1) { + ERROR("fs_mgr_mount_all returned an error\n"); } + exit(child_ret); + } else { + /* fork failed, return an error */ + return -1; } - return 0; + /* ret is 1 if the device is encrypted, 0 if not, and -1 on error */ + if (ret == 1) { + property_set("ro.crypto.state", "encrypted"); + property_set("vold.decrypt", "1"); + } else if (ret == 0) { + property_set("ro.crypto.state", "unencrypted"); + /* If fs_mgr determined this is an unencrypted device, then trigger + * that action. + */ + action_for_each_trigger("nonencrypted", action_add_queue_tail); + } + return ret; } int do_setcon(int nargs, char **args) { -#ifdef HAVE_SELINUX if (is_selinux_enabled() <= 0) return 0; if (setcon(args[1]) < 0) { return -errno; } -#endif return 0; } int do_setenforce(int nargs, char **args) { -#ifdef HAVE_SELINUX if (is_selinux_enabled() <= 0) return 0; if (security_setenforce(atoi(args[1])) < 0) { return -errno; } -#endif return 0; } @@ -643,10 +710,10 @@ out: int do_chown(int nargs, char **args) { /* GID is optional. */ if (nargs == 3) { - if (chown(args[2], decode_uid(args[1]), -1) < 0) + if (_chown(args[2], decode_uid(args[1]), -1) < 0) return -errno; } else if (nargs == 4) { - if (chown(args[3], decode_uid(args[1]), decode_uid(args[2]))) + if (_chown(args[3], decode_uid(args[1]), decode_uid(args[2])) < 0) return -errno; } else { return -1; @@ -669,67 +736,47 @@ static mode_t get_mode(const char *s) { int do_chmod(int nargs, char **args) { mode_t mode = get_mode(args[1]); - if (chmod(args[2], mode) < 0) { + if (_chmod(args[2], mode) < 0) { return -errno; } return 0; } int do_restorecon(int nargs, char **args) { -#ifdef HAVE_SELINUX - char *secontext = NULL; - struct stat sb; int i; - if (is_selinux_enabled() <= 0 || !sehandle) - return 0; - for (i = 1; i < nargs; i++) { - if (lstat(args[i], &sb) < 0) - return -errno; - if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0) - return -errno; - if (lsetfilecon(args[i], secontext) < 0) { - freecon(secontext); + if (restorecon(args[i]) < 0) return -errno; - } - freecon(secontext); } -#endif return 0; } int do_setsebool(int nargs, char **args) { -#ifdef HAVE_SELINUX - SELboolean *b = alloca(nargs * sizeof(SELboolean)); - char *v; - int i; + const char *name = args[1]; + const char *value = args[2]; + SELboolean b; + int ret; if (is_selinux_enabled() <= 0) return 0; - for (i = 1; i < nargs; i++) { - char *name = args[i]; - v = strchr(name, '='); - if (!v) { - ERROR("setsebool: argument %s had no =\n", name); - return -EINVAL; - } - *v++ = 0; - b[i-1].name = name; - if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on")) - b[i-1].value = 1; - else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off")) - b[i-1].value = 0; - else { - ERROR("setsebool: invalid value %s\n", v); - return -EINVAL; - } + b.name = name; + if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on")) + b.value = 1; + else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off")) + b.value = 0; + else { + ERROR("setsebool: invalid value %s\n", value); + return -EINVAL; + } + + if (security_set_boolean_list(1, &b, 0) < 0) { + ret = -errno; + ERROR("setsebool: could not set %s to %s\n", name, value); + return ret; } - if (security_set_boolean_list(nargs - 1, b, 0) < 0) - return -errno; -#endif return 0; } @@ -753,6 +800,8 @@ int do_wait(int nargs, char **args) { if (nargs == 2) { return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT); - } - return -1; + } else if (nargs == 3) { + return wait_for_file(args[1], atoi(args[2])); + } else + return -1; } diff --git a/init/devices.c b/init/devices.c index 3b4d369..69f5fc8 100644 --- a/init/devices.c +++ b/init/devices.c @@ -30,10 +30,9 @@ #include <sys/un.h> #include <linux/netlink.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#endif +#include <selinux/android.h> #include <private/android_filesystem_config.h> #include <sys/time.h> @@ -50,10 +49,9 @@ #define SYSFS_PREFIX "/sys" #define FIRMWARE_DIR1 "/etc/firmware" #define FIRMWARE_DIR2 "/vendor/firmware" +#define FIRMWARE_DIR3 "/firmware/image" -#ifdef HAVE_SELINUX -static struct selabel_handle *sehandle; -#endif +extern struct selabel_handle *sehandle; static int device_fd = -1; @@ -63,6 +61,7 @@ struct uevent { const char *subsystem; const char *firmware; const char *partition_name; + const char *device_name; int partition_num; int major; int minor; @@ -84,7 +83,8 @@ struct perm_node { struct platform_node { char *name; - int name_len; + char *path; + int path_len; struct listnode list; }; @@ -127,6 +127,7 @@ void fixup_sys_perms(const char *upath) char buf[512]; struct listnode *node; struct perms_ *dp; + char *secontext; /* upaths omit the "/sys" that paths in this list * contain, so we add 4 when comparing... @@ -148,6 +149,14 @@ void fixup_sys_perms(const char *upath) INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm); chown(buf, dp->uid, dp->gid); chmod(buf, dp->perm); + if (sehandle) { + secontext = NULL; + selabel_lookup(sehandle, &secontext, buf, 0); + if (secontext) { + setfilecon(buf, secontext); + freecon(secontext); + } + } } } @@ -190,17 +199,15 @@ static void make_device(const char *path, unsigned gid; mode_t mode; dev_t dev; -#ifdef HAVE_SELINUX char *secontext = NULL; -#endif mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); -#ifdef HAVE_SELINUX + if (sehandle) { selabel_lookup(sehandle, &secontext, path, mode); setfscreatecon(secontext); } -#endif + dev = makedev(major, minor); /* Temporarily change egid to avoid race condition setting the gid of the * device node. Unforunately changing the euid would prevent creation of @@ -211,95 +218,76 @@ static void make_device(const char *path, mknod(path, mode, dev); chown(path, uid, -1); setegid(AID_ROOT); -#ifdef HAVE_SELINUX - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } -#endif -} - - -static int make_dir(const char *path, mode_t mode) -{ - int rc; - -#ifdef HAVE_SELINUX - char *secontext = NULL; - - if (sehandle) { - selabel_lookup(sehandle, &secontext, path, mode); - setfscreatecon(secontext); - } -#endif - - rc = mkdir(path, mode); -#ifdef HAVE_SELINUX if (secontext) { freecon(secontext); setfscreatecon(NULL); } -#endif - return rc; } - -static void add_platform_device(const char *name) +static void add_platform_device(const char *path) { - int name_len = strlen(name); + int path_len = strlen(path); struct listnode *node; struct platform_node *bus; + const char *name = path; + + if (!strncmp(path, "/devices/", 9)) { + name += 9; + if (!strncmp(name, "platform/", 9)) + name += 9; + } list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if ((bus->name_len < name_len) && - (name[bus->name_len] == '/') && - !strncmp(name, bus->name, bus->name_len)) + if ((bus->path_len < path_len) && + (path[bus->path_len] == '/') && + !strncmp(path, bus->path, bus->path_len)) /* subdevice of an existing platform, ignore it */ return; } - INFO("adding platform device %s\n", name); + INFO("adding platform device %s (%s)\n", name, path); bus = calloc(1, sizeof(struct platform_node)); - bus->name = strdup(name); - bus->name_len = name_len; + bus->path = strdup(path); + bus->path_len = path_len; + bus->name = bus->path + (name - path); list_add_tail(&platform_names, &bus->list); } /* - * given a name that may start with a platform device, find the length of the + * given a path that may start with a platform device, find the length of the * platform device prefix. If it doesn't start with a platform device, return * 0. */ -static const char *find_platform_device(const char *name) +static struct platform_node *find_platform_device(const char *path) { - int name_len = strlen(name); + int path_len = strlen(path); struct listnode *node; struct platform_node *bus; list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if ((bus->name_len < name_len) && - (name[bus->name_len] == '/') && - !strncmp(name, bus->name, bus->name_len)) - return bus->name; + if ((bus->path_len < path_len) && + (path[bus->path_len] == '/') && + !strncmp(path, bus->path, bus->path_len)) + return bus; } return NULL; } -static void remove_platform_device(const char *name) +static void remove_platform_device(const char *path) { struct listnode *node; struct platform_node *bus; list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if (!strcmp(name, bus->name)) { - INFO("removing platform device %s\n", name); - free(bus->name); + if (!strcmp(path, bus->path)) { + INFO("removing platform device %s\n", bus->name); + free(bus->path); list_remove(node); free(bus); return; @@ -335,6 +323,7 @@ static void parse_event(const char *msg, struct uevent *uevent) uevent->minor = -1; uevent->partition_name = NULL; uevent->partition_num = -1; + uevent->device_name = NULL; /* currently ignoring SEQNUM */ while(*msg) { @@ -362,9 +351,12 @@ static void parse_event(const char *msg, struct uevent *uevent) } else if(!strncmp(msg, "PARTNAME=", 9)) { msg += 9; uevent->partition_name = msg; + } else if(!strncmp(msg, "DEVNAME=", 8)) { + msg += 8; + uevent->device_name = msg; } - /* advance to after the next \0 */ + /* advance to after the next \0 */ while(*msg++) ; } @@ -381,8 +373,10 @@ static char **get_character_device_symlinks(struct uevent *uevent) char **links; int link_num = 0; int width; + struct platform_node *pdev; - if (strncmp(uevent->path, "/devices/platform/", 18)) + pdev = find_platform_device(uevent->path); + if (!pdev) return NULL; links = malloc(sizeof(char *) * 2); @@ -391,7 +385,7 @@ static char **get_character_device_symlinks(struct uevent *uevent) memset(links, 0, sizeof(char *) * 2); /* skip "/devices/platform/<driver>" */ - parent = strchr(uevent->path + 18, '/'); + parent = strchr(uevent->path + pdev->path_len, '/'); if (!*parent) goto err; @@ -428,7 +422,7 @@ err: static char **parse_platform_block_device(struct uevent *uevent) { const char *device; - const char *path; + struct platform_node *pdev; char *slash; int width; char buf[256]; @@ -440,18 +434,16 @@ static char **parse_platform_block_device(struct uevent *uevent) unsigned int size; struct stat info; + pdev = find_platform_device(uevent->path); + if (!pdev) + return NULL; + device = pdev->name; + char **links = malloc(sizeof(char *) * 4); if (!links) return NULL; memset(links, 0, sizeof(char *) * 4); - /* Drop "/devices/platform/" */ - path = uevent->path; - device = path + 18; - device = find_platform_device(device); - if (!device) - goto err; - INFO("found platform device %s\n", device); snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); @@ -473,17 +465,13 @@ static char **parse_platform_block_device(struct uevent *uevent) links[link_num] = NULL; } - slash = strrchr(path, '/'); + slash = strrchr(uevent->path, '/'); if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0) link_num++; else links[link_num] = NULL; return links; - -err: - free(links); - return NULL; } static void handle_device(const char *action, const char *devpath, @@ -516,12 +504,12 @@ static void handle_device(const char *action, const char *devpath, static void handle_platform_device_event(struct uevent *uevent) { - const char *name = uevent->path + 18; /* length of /devices/platform/ */ + const char *path = uevent->path; if (!strcmp(uevent->action, "add")) - add_platform_device(name); + add_platform_device(path); else if (!strcmp(uevent->action, "remove")) - remove_platform_device(name); + remove_platform_device(path); } static const char *parse_device_name(struct uevent *uevent, unsigned int len) @@ -559,7 +547,7 @@ static void handle_block_device_event(struct uevent *uevent) snprintf(devpath, sizeof(devpath), "%s%s", base, name); make_dir(base, 0755); - if (!strncmp(uevent->path, "/devices/platform/", 18)) + if (!strncmp(uevent->path, "/devices/", 9)) links = parse_platform_block_device(uevent); handle_device(uevent->action, devpath, uevent->path, 1, @@ -579,18 +567,39 @@ static void handle_generic_device_event(struct uevent *uevent) if (!strncmp(uevent->subsystem, "usb", 3)) { if (!strcmp(uevent->subsystem, "usb")) { - /* This imitates the file system that would be created - * if we were using devfs instead. - * Minors are broken up into groups of 128, starting at "001" - */ - int bus_id = uevent->minor / 128 + 1; - int device_id = uevent->minor % 128 + 1; - /* build directories */ - make_dir("/dev/bus", 0755); - make_dir("/dev/bus/usb", 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); - make_dir(devpath, 0755); - snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + if (uevent->device_name) { + /* + * create device node provided by kernel if present + * see drivers/base/core.c + */ + char *p = devpath; + snprintf(devpath, sizeof(devpath), "/dev/%s", uevent->device_name); + /* skip leading /dev/ */ + p += 5; + /* build directories */ + while (*p) { + if (*p == '/') { + *p = 0; + make_dir(devpath, 0755); + *p = '/'; + } + p++; + } + } + else { + /* This imitates the file system that would be created + * if we were using devfs instead. + * Minors are broken up into groups of 128, starting at "001" + */ + int bus_id = uevent->minor / 128 + 1; + int device_id = uevent->minor % 128 + 1; + /* build directories */ + make_dir("/dev/bus", 0755); + make_dir("/dev/bus/usb", 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); + make_dir(devpath, 0755); + snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); + } } else { /* ignore other USB events */ return; @@ -598,6 +607,9 @@ static void handle_generic_device_event(struct uevent *uevent) } else if (!strncmp(uevent->subsystem, "graphics", 8)) { base = "/dev/graphics/"; make_dir(base, 0755); + } else if (!strncmp(uevent->subsystem, "drm", 3)) { + base = "/dev/dri/"; + make_dir(base, 0755); } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { base = "/dev/oncrpc/"; make_dir(base, 0755); @@ -634,7 +646,7 @@ static void handle_generic_device_event(struct uevent *uevent) static void handle_device_event(struct uevent *uevent) { - if (!strcmp(uevent->action,"add")) + if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change")) fixup_sys_perms(uevent->path); if (!strncmp(uevent->subsystem, "block", 5)) { @@ -699,7 +711,7 @@ static int is_booting(void) static void process_firmware_event(struct uevent *uevent) { - char *root, *loading, *data, *file1 = NULL, *file2 = NULL; + char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL; int l, loading_fd, data_fd, fw_fd; int booting = is_booting(); @@ -726,6 +738,10 @@ static void process_firmware_event(struct uevent *uevent) if (l == -1) goto data_free_out; + l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware); + if (l == -1) + goto data_free_out; + loading_fd = open(loading, O_WRONLY); if(loading_fd < 0) goto file_free_out; @@ -739,17 +755,20 @@ try_loading_again: if(fw_fd < 0) { fw_fd = open(file2, O_RDONLY); if (fw_fd < 0) { - if (booting) { - /* If we're not fully booted, we may be missing - * filesystems needed for firmware, wait and retry. - */ - usleep(100000); - booting = is_booting(); - goto try_loading_again; + fw_fd = open(file3, O_RDONLY); + if (fw_fd < 0) { + if (booting) { + /* If we're not fully booted, we may be missing + * filesystems needed for firmware, wait and retry. + */ + usleep(100000); + booting = is_booting(); + goto try_loading_again; + } + INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); + write(loading_fd, "-1", 2); + goto data_close_out; } - INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); - write(loading_fd, "-1", 2); - goto data_close_out; } } @@ -870,16 +889,14 @@ void device_init(void) suseconds_t t0, t1; struct stat info; int fd; -#ifdef HAVE_SELINUX - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } - }; - if (is_selinux_enabled() > 0) - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); -#endif - /* is 64K enough? udev uses 16MB! */ - device_fd = uevent_open_socket(64*1024, true); + sehandle = NULL; + if (is_selinux_enabled() > 0) { + sehandle = selinux_android_file_context_handle(); + } + + /* is 256K enough? udev uses 16MB! */ + device_fd = uevent_open_socket(256*1024, true); if(device_fd < 0) return; diff --git a/init/init.c b/init/init.c index 26adcc6..c21a495 100755 --- a/init/init.c +++ b/init/init.c @@ -32,11 +32,9 @@ #include <sys/socket.h> #include <sys/un.h> -#ifdef HAVE_SELINUX -#include <sys/mman.h> #include <selinux/selinux.h> #include <selinux/label.h> -#endif +#include <selinux/android.h> #include <libgen.h> @@ -58,10 +56,10 @@ #include "init_parser.h" #include "util.h" #include "ueventd.h" +#include "watchdogd.h" -#ifdef HAVE_SELINUX struct selabel_handle *sehandle; -#endif +struct selabel_handle *sehandle_prop; static int property_triggers_enabled = 0; @@ -75,10 +73,7 @@ static char hardware[32]; static unsigned revision = 0; static char qemu[32]; -#ifdef HAVE_SELINUX static int selinux_enabled = 1; -static int selinux_enforcing = 0; -#endif static struct action *cur_action = NULL; static struct command *cur_command = NULL; @@ -95,7 +90,7 @@ void notify_service_state(const char *name, const char *state) } static int have_console; -static char *console_name = "/dev/console"; +static char console_name[PROP_VALUE_MAX] = "/dev/console"; static time_t process_needs_restart; static const char *ENV[32]; @@ -134,6 +129,7 @@ static void open_console() if ((fd = open(console_name, O_RDWR)) < 0) { fd = open("/dev/null", O_RDWR); } + ioctl(fd, TIOCSCTTY, 0); dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); @@ -161,10 +157,9 @@ void service_start(struct service *svc, const char *dynamic_args) pid_t pid; int needs_console; int n; -#ifdef HAVE_SELINUX char *scon = NULL; int rc; -#endif + /* starting a service removes it from the disabled or reset * state and immediately takes it out of the restarting * state if it was in there @@ -201,33 +196,39 @@ void service_start(struct service *svc, const char *dynamic_args) return; } -#ifdef HAVE_SELINUX if (is_selinux_enabled() > 0) { - char *mycon = NULL, *fcon = NULL; + if (svc->seclabel) { + scon = strdup(svc->seclabel); + if (!scon) { + ERROR("Out of memory while starting '%s'\n", svc->name); + return; + } + } else { + char *mycon = NULL, *fcon = NULL; - INFO("computing context for service '%s'\n", svc->args[0]); - rc = getcon(&mycon); - if (rc < 0) { - ERROR("could not get context while starting '%s'\n", svc->name); - return; - } + INFO("computing context for service '%s'\n", svc->args[0]); + rc = getcon(&mycon); + if (rc < 0) { + ERROR("could not get context while starting '%s'\n", svc->name); + return; + } - rc = getfilecon(svc->args[0], &fcon); - if (rc < 0) { - ERROR("could not get context while starting '%s'\n", svc->name); - freecon(mycon); - return; - } + rc = getfilecon(svc->args[0], &fcon); + if (rc < 0) { + ERROR("could not get context while starting '%s'\n", svc->name); + freecon(mycon); + return; + } - rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); - freecon(mycon); - freecon(fcon); - if (rc < 0) { - ERROR("could not get context while starting '%s'\n", svc->name); - return; + rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); + freecon(mycon); + freecon(fcon); + if (rc < 0) { + ERROR("could not get context while starting '%s'\n", svc->name); + return; + } } } -#endif NOTICE("starting '%s'\n", svc->name); @@ -239,6 +240,7 @@ void service_start(struct service *svc, const char *dynamic_args) char tmp[32]; int fd, sz; + umask(077); if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(tmp, "%d,%d", dup(fd), sz); @@ -248,9 +250,7 @@ void service_start(struct service *svc, const char *dynamic_args) for (ei = svc->envvars; ei; ei = ei->next) add_environment(ei->name, ei->value); -#ifdef HAVE_SELINUX setsockcreatecon(scon); -#endif for (si = svc->sockets; si; si = si->next) { int socket_type = ( @@ -263,11 +263,9 @@ void service_start(struct service *svc, const char *dynamic_args) } } -#ifdef HAVE_SELINUX freecon(scon); scon = NULL; setsockcreatecon(NULL); -#endif if (svc->ioprio_class != IoSchedClass_NONE) { if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) { @@ -313,15 +311,12 @@ void service_start(struct service *svc, const char *dynamic_args) _exit(127); } } - -#ifdef HAVE_SELINUX if (svc->seclabel) { if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) { ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno)); _exit(127); } } -#endif if (!dynamic_args) { if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) { @@ -348,9 +343,7 @@ void service_start(struct service *svc, const char *dynamic_args) _exit(127); } -#ifdef HAVE_SELINUX freecon(scon); -#endif if (pid < 0) { ERROR("failed to start '%s'\n", svc->name); @@ -447,7 +440,7 @@ static void restart_processes() static void msg_start(const char *name) { - struct service *svc; + struct service *svc = NULL; char *tmp = NULL; char *args = NULL; @@ -455,11 +448,13 @@ static void msg_start(const char *name) svc = service_find_by_name(name); else { tmp = strdup(name); - args = strchr(tmp, ':'); - *args = '\0'; - args++; + if (tmp) { + args = strchr(tmp, ':'); + *args = '\0'; + args++; - svc = service_find_by_name(tmp); + svc = service_find_by_name(tmp); + } } if (svc) { @@ -574,11 +569,9 @@ static int keychord_init_action(int nargs, char **args) static int console_init_action(int nargs, char **args) { int fd; - char tmp[PROP_VALUE_MAX]; if (console[0]) { - snprintf(tmp, sizeof(tmp), "/dev/%s", console); - console_name = strdup(tmp); + snprintf(console_name, sizeof(console_name), "/dev/%s", console); } fd = open(console_name, O_RDWR); @@ -621,13 +614,9 @@ static void import_kernel_nv(char *name, int for_emulator) *value++ = 0; if (name_len == 0) return; -#ifdef HAVE_SELINUX - if (!strcmp(name,"enforcing")) { - selinux_enforcing = atoi(value); - } else if (!strcmp(name,"selinux")) { + if (!strcmp(name,"selinux")) { selinux_enabled = atoi(value); } -#endif if (for_emulator) { /* in the emulator, export any kernel option with the @@ -656,7 +645,7 @@ static void import_kernel_nv(char *name, int for_emulator) static void export_kernel_boot_props(void) { char tmp[PROP_VALUE_MAX]; - const char *pval; + int ret; unsigned i; struct { const char *src_prop; @@ -666,27 +655,30 @@ static void export_kernel_boot_props(void) { "ro.boot.serialno", "ro.serialno", "", }, { "ro.boot.mode", "ro.bootmode", "unknown", }, { "ro.boot.baseband", "ro.baseband", "unknown", }, - { "ro.boot.carrier", "ro.carrier", "unknown", }, { "ro.boot.bootloader", "ro.bootloader", "unknown", }, }; for (i = 0; i < ARRAY_SIZE(prop_map); i++) { - pval = property_get(prop_map[i].src_prop); - property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val); + ret = property_get(prop_map[i].src_prop, tmp); + if (ret > 0) + property_set(prop_map[i].dest_prop, tmp); + else + property_set(prop_map[i].dest_prop, prop_map[i].def_val); } - pval = property_get("ro.boot.console"); - if (pval) - strlcpy(console, pval, sizeof(console)); + ret = property_get("ro.boot.console", tmp); + if (ret) + strlcpy(console, tmp, sizeof(console)); /* save a copy for init's usage during boot */ - strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode)); + property_get("ro.bootmode", tmp); + strlcpy(bootmode, tmp, sizeof(bootmode)); /* if this was given on kernel command line, override what we read * before (e.g. from /proc/cpuinfo), if anything */ - pval = property_get("ro.boot.hardware"); - if (pval) - strlcpy(hardware, pval, sizeof(hardware)); + ret = property_get("ro.boot.hardware", tmp); + if (ret) + strlcpy(hardware, tmp, sizeof(hardware)); property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); @@ -776,96 +768,63 @@ static int bootchart_init_action(int nargs, char **args) } #endif -#ifdef HAVE_SELINUX -void selinux_load_policy(void) -{ - const char path_prefix[] = "/sepolicy"; - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } - }; - char path[PATH_MAX]; - int fd, rc, vers; - struct stat sb; - void *map; +static const struct selinux_opt seopts_prop[] = { + { SELABEL_OPT_PATH, "/data/security/property_contexts" }, + { SELABEL_OPT_PATH, "/property_contexts" }, + { 0, NULL } +}; - sehandle = NULL; - if (!selinux_enabled) { - INFO("SELinux: Disabled by command line option\n"); - return; +struct selabel_handle* selinux_android_prop_context_handle(void) +{ + int i = 0; + struct selabel_handle* sehandle = NULL; + while ((sehandle == NULL) && seopts_prop[i].value) { + sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP, &seopts_prop[i], 1); + i++; } - mkdir(SELINUXMNT, 0755); - if (mount("selinuxfs", SELINUXMNT, "selinuxfs", 0, NULL)) { - if (errno == ENODEV) { - /* SELinux not enabled in kernel */ - return; - } - ERROR("SELinux: Could not mount selinuxfs: %s\n", + if (!sehandle) { + ERROR("SELinux: Could not load property_contexts: %s\n", strerror(errno)); - return; + return NULL; } - set_selinuxmnt(SELINUXMNT); + INFO("SELinux: Loaded property contexts from %s\n", seopts_prop[i - 1].value); + return sehandle; +} - vers = security_policyvers(); - if (vers <= 0) { - ERROR("SELinux: Unable to read policy version\n"); - return; - } - INFO("SELinux: Maximum supported policy version: %d\n", vers); - - snprintf(path, sizeof(path), "%s.%d", - path_prefix, vers); - fd = open(path, O_RDONLY); - while (fd < 0 && errno == ENOENT && --vers) { - snprintf(path, sizeof(path), "%s.%d", - path_prefix, vers); - fd = open(path, O_RDONLY); - } - if (fd < 0) { - ERROR("SELinux: Could not open %s: %s\n", - path, strerror(errno)); - return; - } - if (fstat(fd, &sb) < 0) { - ERROR("SELinux: Could not stat %s: %s\n", - path, strerror(errno)); - return; - } - map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (map == MAP_FAILED) { - ERROR("SELinux: Could not map %s: %s\n", - path, strerror(errno)); - return; - } +void selinux_init_all_handles(void) +{ + sehandle = selinux_android_file_context_handle(); + sehandle_prop = selinux_android_prop_context_handle(); +} - rc = security_load_policy(map, sb.st_size); - if (rc < 0) { - ERROR("SELinux: Could not load policy: %s\n", - strerror(errno)); - return; +int selinux_reload_policy(void) +{ + if (!selinux_enabled) { + return -1; } - rc = security_setenforce(selinux_enforcing); - if (rc < 0) { - ERROR("SELinux: Could not set enforcing mode to %s: %s\n", - selinux_enforcing ? "enforcing" : "permissive", strerror(errno)); - return; + INFO("SELinux: Attempting to reload policy files\n"); + + if (selinux_android_reload_policy() == -1) { + return -1; } - munmap(map, sb.st_size); - close(fd); - INFO("SELinux: Loaded policy from %s\n", path); + if (sehandle) + selabel_close(sehandle); - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); - if (!sehandle) { - ERROR("SELinux: Could not load file_contexts: %s\n", - strerror(errno)); - return; - } - INFO("SELinux: Loaded file contexts from %s\n", seopts[0].value); - return; + if (sehandle_prop) + selabel_close(sehandle_prop); + + selinux_init_all_handles(); + return 0; +} + +int audit_callback(void *data, security_class_t cls, char *buf, size_t len) +{ + snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data); + return 0; } -#endif int main(int argc, char **argv) { @@ -882,6 +841,9 @@ int main(int argc, char **argv) if (!strcmp(basename(argv[0]), "ueventd")) return ueventd_main(argc, argv); + if (!strcmp(basename(argv[0]), "watchdogd")) + return watchdogd_main(argc, argv); + /* clear the umask */ umask(0); @@ -917,10 +879,30 @@ int main(int argc, char **argv) process_kernel_cmdline(); -#ifdef HAVE_SELINUX + union selinux_callback cb; + cb.func_log = klog_write; + selinux_set_callback(SELINUX_CB_LOG, cb); + + cb.func_audit = audit_callback; + selinux_set_callback(SELINUX_CB_AUDIT, cb); + INFO("loading selinux policy\n"); - selinux_load_policy(); -#endif + if (selinux_enabled) { + if (selinux_android_load_policy() < 0) { + selinux_enabled = 0; + INFO("SELinux: Disabled due to failed policy load\n"); + } else { + selinux_init_all_handles(); + } + } else { + INFO("SELinux: Disabled by command line option\n"); + } + /* These directories were necessarily created before initial policy load + * and therefore need their security context restored to the proper value. + * This must happen before /dev is populated by ueventd. + */ + restorecon("/dev"); + restorecon("/dev/socket"); is_charger = !strcmp(bootmode, "charger"); diff --git a/init/init.h b/init/init.h index c20864a..aa6a4ab 100644 --- a/init/init.h +++ b/init/init.h @@ -96,9 +96,7 @@ struct service { gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; -#ifdef HAVE_SELINUX char *seclabel; -#endif struct socketinfo *sockets; struct svcenvinfo *envvars; @@ -138,8 +136,8 @@ void property_changed(const char *name, const char *value); int load_565rle_image( char *file_name ); -#ifdef HAVE_SELINUX extern struct selabel_handle *sehandle; -#endif +extern struct selabel_handle *sehandle_prop; +extern int selinux_reload_policy(void); #endif /* _INIT_INIT_H */ diff --git a/init/init_parser.c b/init/init_parser.c index f538450..cce1093 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -122,6 +122,7 @@ int lookup_keyword(const char *s) break; case 'm': if (!strcmp(s, "kdir")) return K_mkdir; + if (!strcmp(s, "ount_all")) return K_mount_all; if (!strcmp(s, "ount")) return K_mount; break; case 'o': @@ -205,8 +206,9 @@ int expand_props(char *dst, const char *src, int dst_size) while (*src_ptr && left > 0) { char *c; char prop[PROP_NAME_MAX + 1]; - const char *prop_val; + char prop_val[PROP_VALUE_MAX]; int prop_len = 0; + int prop_val_len; c = strchr(src_ptr, '$'); if (!c) { @@ -264,14 +266,14 @@ int expand_props(char *dst, const char *src, int dst_size) goto err; } - prop_val = property_get(prop); - if (!prop_val) { + prop_val_len = property_get(prop, prop_val); + if (!prop_val_len) { ERROR("property '%s' doesn't exist while expanding '%s'\n", prop, src); goto err; } - ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val)); + ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len); if (ret < 0) goto err_nospace; src_ptr = c; @@ -542,7 +544,7 @@ void queue_all_property_triggers() const char* equals = strchr(name, '='); if (equals) { char prop_name[PROP_NAME_MAX + 1]; - const char* value; + char value[PROP_VALUE_MAX]; int length = equals - name; if (length > PROP_NAME_MAX) { ERROR("property name too long in trigger %s", act->name); @@ -551,9 +553,8 @@ void queue_all_property_triggers() prop_name[length] = 0; /* does the property exist, and match the trigger value? */ - value = property_get(prop_name); - if (value && (!strcmp(equals + 1, value) || - !strcmp(equals + 1, "*"))) { + property_get(prop_name, value); + if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) { action_add_queue_tail(act); } } @@ -570,6 +571,7 @@ void queue_builtin_action(int (*func)(int nargs, char **args), char *name) act = calloc(1, sizeof(*act)); act->name = name; list_init(&act->commands); + list_init(&act->qlist); cmd = calloc(1, sizeof(*cmd)); cmd->func = func; @@ -582,7 +584,9 @@ void queue_builtin_action(int (*func)(int nargs, char **args), char *name) void action_add_queue_tail(struct action *act) { - list_add_tail(&action_queue, &act->qlist); + if (list_empty(&act->qlist)) { + list_add_tail(&action_queue, &act->qlist); + } } struct action *action_remove_queue_head(void) @@ -593,6 +597,7 @@ struct action *action_remove_queue_head(void) struct listnode *node = list_head(&action_queue); struct action *act = node_to_item(node, struct action, qlist); list_remove(node); + list_init(node); return act; } } @@ -798,13 +803,11 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args } break; case K_seclabel: -#ifdef HAVE_SELINUX if (nargs != 2) { parse_error(state, "seclabel option requires a label string\n"); } else { svc->seclabel = args[1]; } -#endif break; default: @@ -826,6 +829,7 @@ static void *parse_action(struct parse_state *state, int nargs, char **args) act = calloc(1, sizeof(*act)); act->name = args[1]; list_init(&act->commands); + list_init(&act->qlist); list_add_tail(&action_list, &act->alist); /* XXX add to hash */ return act; diff --git a/init/keychords.c b/init/keychords.c index aab0819..d18a6e4 100644 --- a/init/keychords.c +++ b/init/keychords.c @@ -95,24 +95,23 @@ void keychord_init() void handle_keychord() { struct service *svc; - const char* debuggable; - const char* adb_enabled; + char debuggable[PROP_VALUE_MAX]; + char adb_enabled[PROP_VALUE_MAX]; int ret; __u16 id; // only handle keychords if ro.debuggable is set or adb is enabled. // the logic here is that bugreports should be enabled in userdebug or eng builds // and on user builds for users that are developers. - debuggable = property_get("ro.debuggable"); - adb_enabled = property_get("init.svc.adbd"); + property_get("ro.debuggable", debuggable); + property_get("init.svc.adbd", adb_enabled); ret = read(keychord_fd, &id, sizeof(id)); if (ret != sizeof(id)) { ERROR("could not read keychord id\n"); return; } - if ((debuggable && !strcmp(debuggable, "1")) || - (adb_enabled && !strcmp(adb_enabled, "running"))) { + if (!strcmp(debuggable, "1") || !strcmp(adb_enabled, "running")) { svc = service_find_by_keychord(id); if (svc) { INFO("starting service %s from keychord\n", svc->name); diff --git a/init/keywords.h b/init/keywords.h index 307c084..f188db5 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -12,6 +12,7 @@ int do_hostname(int nargs, char **args); int do_ifup(int nargs, char **args); int do_insmod(int nargs, char **args); int do_mkdir(int nargs, char **args); +int do_mount_all(int nargs, char **args); int do_mount(int nargs, char **args); int do_restart(int nargs, char **args); int do_restorecon(int nargs, char **args); @@ -60,6 +61,7 @@ enum { KEYWORD(import, SECTION, 1, 0) KEYWORD(keycodes, OPTION, 0, 0) KEYWORD(mkdir, COMMAND, 1, do_mkdir) + KEYWORD(mount_all, COMMAND, 1, do_mount_all) KEYWORD(mount, COMMAND, 3, do_mount) KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) @@ -76,7 +78,7 @@ enum { KEYWORD(setkey, COMMAND, 0, do_setkey) KEYWORD(setprop, COMMAND, 2, do_setprop) KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) - KEYWORD(setsebool, COMMAND, 1, do_setsebool) + KEYWORD(setsebool, COMMAND, 2, do_setsebool) KEYWORD(socket, OPTION, 0, 0) KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) diff --git a/init/property_service.c b/init/property_service.c index 059db97..86e35f1 100644..100755 --- a/init/property_service.c +++ b/init/property_service.c @@ -40,6 +40,9 @@ #include <sys/atomics.h> #include <private/android_filesystem_config.h> +#include <selinux/selinux.h> +#include <selinux/label.h> + #include "property_service.h" #include "init.h" #include "util.h" @@ -76,8 +79,10 @@ struct { { "sys.", AID_SYSTEM, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, + { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, + { "debug.", AID_SYSTEM, 0 }, { "debug.", AID_SHELL, 0 }, { "log.", AID_SHELL, 0 }, { "service.adb.root", AID_SHELL, 0 }, @@ -85,6 +90,8 @@ struct { { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, + { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, + { "selinux." , AID_SYSTEM, 0 }, { NULL, 0, 0 } }; @@ -116,7 +123,7 @@ static int init_workspace(workspace *w, size_t size) /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let's do that... */ - fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600); + fd = open("/dev/__properties__", O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (fd < 0) return -1; @@ -129,7 +136,7 @@ static int init_workspace(workspace *w, size_t size) close(fd); - fd = open("/dev/__properties__", O_RDONLY); + fd = open("/dev/__properties__", O_RDONLY | O_NOFOLLOW); if (fd < 0) return -1; @@ -145,23 +152,11 @@ out: return -1; } -/* (8 header words + 247 toc words) = 1020 bytes */ -/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */ - -#define PA_COUNT_MAX 247 -#define PA_INFO_START 1024 -#define PA_SIZE 32768 - static workspace pa_workspace; -static prop_info *pa_info_array; - -extern prop_area *__system_property_area__; static int init_property_area(void) { - prop_area *pa; - - if(pa_info_array) + if (property_area_inited) return -1; if(init_workspace(&pa_workspace, PA_SIZE)) @@ -169,25 +164,54 @@ static int init_property_area(void) fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); - pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START); + __system_property_area_init(pa_workspace.data); - pa = pa_workspace.data; - memset(pa, 0, PA_SIZE); - pa->magic = PROP_AREA_MAGIC; - pa->version = PROP_AREA_VERSION; - - /* plug into the lib property services */ - __system_property_area__ = pa; property_area_inited = 1; return 0; } -static void update_prop_info(prop_info *pi, const char *value, unsigned len) +static int check_mac_perms(const char *name, char *sctx) { - pi->serial = pi->serial | 1; - memcpy(pi->value, value, len + 1); - pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); - __futex_wake(&pi->serial, INT32_MAX); + if (is_selinux_enabled() <= 0) + return 1; + + char *tctx = NULL; + const char *class = "property_service"; + const char *perm = "set"; + int result = 0; + + if (!sctx) + goto err; + + if (!sehandle_prop) + goto err; + + if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) + goto err; + + if (selinux_check_access(sctx, tctx, class, perm, name) == 0) + result = 1; + + freecon(tctx); + err: + return result; +} + +static int check_control_mac_perms(const char *name, char *sctx) +{ + /* + * Create a name prefix out of ctl.<service name> + * The new prefix allows the use of the existing + * property service backend labeling while avoiding + * mislabels based on true property prefixes. + */ + char ctl_name[PROP_VALUE_MAX+4]; + int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name); + + if (ret < 0 || (size_t) ret >= sizeof(ctl_name)) + return 0; + + return check_mac_perms(ctl_name, sctx); } /* @@ -196,17 +220,18 @@ static void update_prop_info(prop_info *pi, const char *value, unsigned len) * * Returns 1 if uid allowed, 0 otherwise. */ -static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) { +static int check_control_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) { + int i; if (uid == AID_SYSTEM || uid == AID_ROOT) - return 1; + return check_control_mac_perms(name, sctx); /* Search the ACL */ for (i = 0; control_perms[i].service; i++) { if (strcmp(control_perms[i].service, name) == 0) { if ((uid && control_perms[i].uid == uid) || (gid && control_perms[i].gid == gid)) { - return 1; + return check_control_mac_perms(name, sctx); } } } @@ -217,22 +242,22 @@ static int check_control_perms(const char *name, unsigned int uid, unsigned int * Checks permissions for setting system properties. * Returns 1 if uid allowed, 0 otherwise. */ -static int check_perms(const char *name, unsigned int uid, unsigned int gid) +static int check_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) { int i; - if (uid == 0) - return 1; - if(!strncmp(name, "ro.", 3)) name +=3; + if (uid == 0) + return check_mac_perms(name, sctx); + for (i = 0; property_perms[i].prefix; i++) { - int tmp; if (strncmp(property_perms[i].prefix, name, strlen(property_perms[i].prefix)) == 0) { if ((uid && property_perms[i].uid == uid) || (gid && property_perms[i].gid == gid)) { - return 1; + + return check_mac_perms(name, sctx); } } } @@ -240,30 +265,19 @@ static int check_perms(const char *name, unsigned int uid, unsigned int gid) return 0; } -const char* property_get(const char *name) +int __property_get(const char *name, char *value) { - prop_info *pi; - - if(strlen(name) >= PROP_NAME_MAX) return 0; - - pi = (prop_info*) __system_property_find(name); - - if(pi != 0) { - return pi->value; - } else { - return 0; - } + return __system_property_get(name, value); } static void write_persistent_property(const char *name, const char *value) { - const char *tempPath = PERSISTENT_PROPERTY_DIR "/.temp"; + char tempPath[PATH_MAX]; char path[PATH_MAX]; - int fd, length; - - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); + int fd; - fd = open(tempPath, O_WRONLY|O_CREAT|O_TRUNC, 0600); + snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR); + fd = mkstemp(tempPath); if (fd < 0) { ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno); return; @@ -271,6 +285,7 @@ static void write_persistent_property(const char *name, const char *value) write(fd, value, strlen(value)); close(fd); + snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); if (rename(tempPath, path)) { unlink(tempPath); ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path); @@ -279,11 +294,11 @@ static void write_persistent_property(const char *name, const char *value) int property_set(const char *name, const char *value) { - prop_area *pa; prop_info *pi; + int ret; - int namelen = strlen(name); - int valuelen = strlen(value); + size_t namelen = strlen(name); + size_t valuelen = strlen(value); if(namelen >= PROP_NAME_MAX) return -1; if(valuelen >= PROP_VALUE_MAX) return -1; @@ -295,25 +310,13 @@ int property_set(const char *name, const char *value) /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; - pa = __system_property_area__; - update_prop_info(pi, value, valuelen); - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); + __system_property_update(pi, value, valuelen); } else { - pa = __system_property_area__; - if(pa->count == PA_COUNT_MAX) return -1; - - pi = pa_info_array + pa->count; - pi->serial = (valuelen << 24); - memcpy(pi->name, name, namelen + 1); - memcpy(pi->value, value, valuelen + 1); - - pa->toc[pa->count] = - (namelen << 24) | (((unsigned) pi) - ((unsigned) pa)); - - pa->count++; - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); + ret = __system_property_add(name, namelen, value, valuelen); + if (ret < 0) { + ERROR("Failed to set '%s'='%s'", name, value); + return ret; + } } /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { @@ -333,26 +336,14 @@ int property_set(const char *name, const char *value) * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); + } else if (strcmp("selinux.reload_policy", name) == 0 && + strcmp("1", value) == 0) { + selinux_reload_policy(); } property_changed(name, value); return 0; } -static int property_list(void (*propfn)(const char *key, const char *value, void *cookie), - void *cookie) -{ - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; - const prop_info *pi; - unsigned n; - - for(n = 0; (pi = __system_property_find_nth(n)); n++) { - __system_property_read(pi, name, value); - propfn(name, value, cookie); - } - return 0; -} - void handle_property_set_fd() { prop_msg msg; @@ -363,6 +354,7 @@ void handle_property_set_fd() struct sockaddr_un addr; socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); + char * source_ctx = NULL; if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; @@ -371,13 +363,13 @@ void handle_property_set_fd() /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); - ERROR("Unable to recieve socket options\n"); + ERROR("Unable to receive socket options\n"); return; } r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { - ERROR("sys_prop: mis-match msg size recieved: %d expected: %d errno: %d\n", + ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; @@ -388,18 +380,20 @@ void handle_property_set_fd() msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; + getpeercon(s, &source_ctx); + if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling // ctl.* properties. close(s); - if (check_control_perms(msg.value, cr.uid, cr.gid)) { + if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) { handle_control_message((char*) msg.name + 4, (char*) msg.value); } else { ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n", msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); } } else { - if (check_perms(msg.name, cr.uid, cr.gid)) { + if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) { property_set((char*) msg.name, (char*) msg.value); } else { ERROR("sys_prop: permission denied uid:%d name:%s\n", @@ -411,6 +405,7 @@ void handle_property_set_fd() // the property is written to memory. close(s); } + freecon(source_ctx); break; default: @@ -468,12 +463,14 @@ static void load_properties_from_file(const char *fn) static void load_persistent_properties() { DIR* dir = opendir(PERSISTENT_PROPERTY_DIR); + int dir_fd; struct dirent* entry; - char path[PATH_MAX]; char value[PROP_VALUE_MAX]; int fd, length; + struct stat sb; if (dir) { + dir_fd = dirfd(dir); while ((entry = readdir(dir)) != NULL) { if (strncmp("persist.", entry->d_name, strlen("persist."))) continue; @@ -482,20 +479,39 @@ static void load_persistent_properties() continue; #endif /* open the file and read the property value */ - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, entry->d_name); - fd = open(path, O_RDONLY); - if (fd >= 0) { - length = read(fd, value, sizeof(value) - 1); - if (length >= 0) { - value[length] = 0; - property_set(entry->d_name, value); - } else { - ERROR("Unable to read persistent property file %s errno: %d\n", path, errno); - } + fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW); + if (fd < 0) { + ERROR("Unable to open persistent property file \"%s\" errno: %d\n", + entry->d_name, errno); + continue; + } + if (fstat(fd, &sb) < 0) { + ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno); + close(fd); + continue; + } + + // File must not be accessible to others, be owned by root/root, and + // not be a hard link to any other file. + if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) + || (sb.st_uid != 0) + || (sb.st_gid != 0) + || (sb.st_nlink != 1)) { + ERROR("skipping insecure property file %s (uid=%lu gid=%lu nlink=%d mode=%o)\n", + entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); close(fd); + continue; + } + + length = read(fd, value, sizeof(value) - 1); + if (length >= 0) { + value[length] = 0; + property_set(entry->d_name, value); } else { - ERROR("Unable to open persistent property file %s errno: %d\n", path, errno); + ERROR("Unable to read persistent property file %s errno: %d\n", + entry->d_name, errno); } + close(fd); } closedir(dir); } else { @@ -520,6 +536,19 @@ int properties_inited(void) return property_area_inited; } +static void load_override_properties() { +#ifdef ALLOW_LOCAL_PROP_OVERRIDE + char debuggable[PROP_VALUE_MAX]; + int ret; + + ret = property_get("ro.debuggable", debuggable); + if (ret && (strcmp(debuggable, "1") == 0)) { + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + } +#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ +} + + /* When booting an encrypted system, /data is not mounted when the * property service is started, so any properties stored there are * not loaded. Vold triggers init to load these properties once it @@ -527,7 +556,7 @@ int properties_inited(void) */ void load_persist_props(void) { - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); } @@ -538,7 +567,7 @@ void start_property_service(void) load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); diff --git a/init/property_service.h b/init/property_service.h index b9d1bf6..46cbd8f 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -18,6 +18,7 @@ #define _INIT_PROPERTY_H #include <stdbool.h> +#include <sys/system_properties.h> extern void handle_property_set_fd(void); extern void property_init(void); @@ -25,9 +26,25 @@ extern void property_load_boot_defaults(void); extern void load_persist_props(void); extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); -extern const char* property_get(const char *name); +extern int __property_get(const char *name, char *value); extern int property_set(const char *name, const char *value); extern int properties_inited(); int get_property_set_fd(void); +extern void __property_get_size_error() + __attribute__((__error__("property_get called with too small buffer"))); + +static inline +__attribute__ ((always_inline)) +__attribute__ ((gnu_inline)) +__attribute__ ((artificial)) +int property_get(const char *name, char *value) +{ + size_t value_len = __builtin_object_size(value, 0); + if (value_len != PROP_VALUE_MAX) + __property_get_size_error(); + + return __property_get(name, value); +} + #endif /* _INIT_PROPERTY_H */ diff --git a/init/readme.txt b/init/readme.txt index df524a6..7a5997d 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -88,6 +88,13 @@ group <groupname> [ <groupname> ]* supplemental groups of the process (via setgroups()). Currently defaults to root. (??? probably should default to nobody) +seclabel <securitycontext> + Change to securitycontext before exec'ing this service. + Primarily for use by services run from the rootfs, e.g. ueventd, adbd. + Services on the system partition can instead use policy-defined transitions + based on their file security context. + If not specified and no transition is defined in policy, defaults to the init context. + oneshot Do not restart the service when it exits. @@ -182,6 +189,21 @@ mount <type> <device> <dir> [ <mountoption> ]* device by name. <mountoption>s include "ro", "rw", "remount", "noatime", ... +restorecon <path> + Restore the file named by <path> to the security context specified + in the file_contexts configuration. + Not required for directories created by the init.rc as these are + automatically labeled correctly by init. + +setcon <securitycontext> + Set the current process security context to the specified string. + This is typically only used from early-init to set the init context + before any other process is started. + +setenforce 0|1 + Set the SELinux system-wide enforcing status. + 0 is permissive (i.e. log but do not deny), 1 is enforcing. + setkey TBD @@ -191,6 +213,10 @@ setprop <name> <value> setrlimit <resource> <cur> <max> Set the rlimit for a resource. +setsebool <name> <value> + Set SELinux boolean <name> to <value>. + <value> may be 1|true|on or 0|false|off + start <service> Start a service running if it is not already running. @@ -207,6 +233,11 @@ trigger <event> Trigger an event. Used to queue an action from another action. +wait <path> [ <timeout> ] + Poll for the existence of the given file and return when found, + or the timeout has been reached. If timeout is not specified it + currently defaults to five seconds. + write <path> <string> [ <string> ]* Open the file at <path> and write one or more strings to it with write(2) diff --git a/init/signal_handler.c b/init/signal_handler.c index 672904d..d31ad63 100644 --- a/init/signal_handler.c +++ b/init/signal_handler.c @@ -133,11 +133,9 @@ void signal_init(void) int s[2]; struct sigaction act; - + memset(&act, 0, sizeof(act)); act.sa_handler = sigchld_handler; act.sa_flags = SA_NOCLDSTOP; - act.sa_mask = 0; - act.sa_restorer = NULL; sigaction(SIGCHLD, &act, 0); /* create a signalling mechanism for the sigchld handler */ diff --git a/init/ueventd.c b/init/ueventd.c index ecf3b9b..a41c31e 100644 --- a/init/ueventd.c +++ b/init/ueventd.c @@ -53,11 +53,18 @@ int ueventd_main(int argc, char **argv) int nr; char tmp[32]; - /* Prevent fire-and-forget children from becoming zombies. - * If we should need to wait() for some children in the future - * (as opposed to none right now), double-forking here instead - * of ignoring SIGCHLD may be the better solution. - */ + /* + * init sets the umask to 077 for forked processes. We need to + * create files with exact permissions, without modification by + * the umask. + */ + umask(000); + + /* Prevent fire-and-forget children from becoming zombies. + * If we should need to wait() for some children in the future + * (as opposed to none right now), double-forking here instead + * of ignoring SIGCHLD may be the better solution. + */ signal(SIGCHLD, SIG_IGN); open_devnull_stdio(); @@ -98,7 +105,7 @@ static int get_android_id(const char *id) for (i = 0; i < ARRAY_SIZE(android_ids); i++) if (!strcmp(id, android_ids[i].name)) return android_ids[i].aid; - return 0; + return -1; } void set_device_permission(int nargs, char **args) diff --git a/init/util.c b/init/util.c index 3a4b10b..918bc05 100755 --- a/init/util.c +++ b/init/util.c @@ -23,9 +23,7 @@ #include <errno.h> #include <time.h> -#ifdef HAVE_SELINUX #include <selinux/label.h> -#endif #include <sys/stat.h> #include <sys/types.h> @@ -47,7 +45,7 @@ */ static unsigned int android_name_to_id(const char *name) { - struct android_id_info *info = android_ids; + const struct android_id_info *info = android_ids; unsigned int n; for (n = 0; n < android_id_count; n++) { @@ -89,9 +87,7 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) { struct sockaddr_un addr; int fd, ret; -#ifdef HAVE_SELINUX char *secon; -#endif fd = socket(PF_UNIX, type, 0); if (fd < 0) { @@ -110,14 +106,12 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_close; } -#ifdef HAVE_SELINUX secon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(secon); } -#endif ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); if (ret) { @@ -125,10 +119,8 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) goto out_unlink; } -#ifdef HAVE_SELINUX setfscreatecon(NULL); freecon(secon); -#endif chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); @@ -151,11 +143,23 @@ void *read_file(const char *fn, unsigned *_sz) char *data; int sz; int fd; + struct stat sb; data = 0; fd = open(fn, O_RDONLY); if(fd < 0) return 0; + // for security reasons, disallow world-writable + // or group-writable files + if (fstat(fd, &sb) < 0) { + ERROR("fstat failed for '%s'\n", fn); + goto oops; + } + if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) { + ERROR("skipping insecure file '%s'\n", fn); + goto oops; + } + sz = lseek(fd, 0, SEEK_END); if(sz < 0) goto oops; @@ -290,12 +294,12 @@ int mkdir_recursive(const char *pathname, mode_t mode) memcpy(buf, pathname, width); buf[width] = 0; if (stat(buf, &info) != 0) { - ret = mkdir(buf, mode); + ret = make_dir(buf, mode); if (ret && errno != EEXIST) return ret; } } - ret = mkdir(pathname, mode); + ret = make_dir(pathname, mode); if (ret && errno != EEXIST) return ret; return 0; @@ -451,3 +455,47 @@ void import_kernel_cmdline(int in_qemu, ptr = x; } } + +int make_dir(const char *path, mode_t mode) +{ + int rc; + + char *secontext = NULL; + + if (sehandle) { + selabel_lookup(sehandle, &secontext, path, mode); + setfscreatecon(secontext); + } + + rc = mkdir(path, mode); + + if (secontext) { + int save_errno = errno; + freecon(secontext); + setfscreatecon(NULL); + errno = save_errno; + } + + return rc; +} + +int restorecon(const char *pathname) +{ + char *secontext = NULL; + struct stat sb; + int i; + + if (is_selinux_enabled() <= 0 || !sehandle) + return 0; + + if (lstat(pathname, &sb) < 0) + return -errno; + if (selabel_lookup(sehandle, &secontext, pathname, sb.st_mode) < 0) + return -errno; + if (lsetfilecon(pathname, secontext) < 0) { + freecon(secontext); + return -errno; + } + freecon(secontext); + return 0; +} diff --git a/init/util.h b/init/util.h index 9247739..45905b6 100644 --- a/init/util.h +++ b/init/util.h @@ -39,4 +39,6 @@ int wait_for_file(const char *filename, int timeout); void open_devnull_stdio(void); void get_hardware_name(char *hardware, unsigned int *revision); void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu)); +int make_dir(const char *path, mode_t mode); +int restorecon(const char *pathname); #endif diff --git a/init/watchdogd.c b/init/watchdogd.c new file mode 100644 index 0000000..fb53836 --- /dev/null +++ b/init/watchdogd.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include <linux/watchdog.h> + +#include "log.h" +#include "util.h" + +#define DEV_NAME "/dev/watchdog" + +int watchdogd_main(int argc, char **argv) +{ + int fd; + int ret; + int interval = 10; + int margin = 10; + int timeout; + + open_devnull_stdio(); + klog_init(); + + INFO("Starting watchdogd\n"); + + if (argc >= 2) + interval = atoi(argv[1]); + + if (argc >= 3) + margin = atoi(argv[2]); + + timeout = interval + margin; + + fd = open(DEV_NAME, O_RDWR); + if (fd < 0) { + ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno)); + return 1; + } + + ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno)); + ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout); + if (ret) { + ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno)); + } else { + if (timeout > margin) + interval = timeout - margin; + else + interval = 1; + ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n", + timeout, interval, margin); + } + } + + while(1) { + write(fd, "", 1); + sleep(interval); + } +} + diff --git a/nexus/NexusCommand.cpp b/init/watchdogd.h index 541eeeb..8b48ab8 100644 --- a/nexus/NexusCommand.cpp +++ b/init/watchdogd.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,8 +14,9 @@ * limitations under the License. */ -#include "NexusCommand.h" +#ifndef _INIT_WATCHDOGD_H_ +#define _INIT_WATCHDOGD_H_ -NexusCommand::NexusCommand(const char *cmd) : - FrameworkCommand(cmd) { -} +int watchdogd_main(int argc, char **argv); + +#endif diff --git a/libcorkscrew/Android.mk b/libcorkscrew/Android.mk new file mode 100644 index 0000000..25512e2 --- /dev/null +++ b/libcorkscrew/Android.mk @@ -0,0 +1,98 @@ +# 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) + +generic_src_files := \ + backtrace.c \ + backtrace-helper.c \ + demangle.c \ + map_info.c \ + ptrace.c \ + symbol_table.c + +arm_src_files := \ + arch-arm/backtrace-arm.c \ + arch-arm/ptrace-arm.c + +x86_src_files := \ + arch-x86/backtrace-x86.c \ + arch-x86/ptrace-x86.c + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(generic_src_files) + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += $(arm_src_files) +LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH +endif +ifeq ($(TARGET_ARCH),x86) +LOCAL_SRC_FILES += $(x86_src_files) +LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH +endif +ifeq ($(TARGET_ARCH),mips) +LOCAL_SRC_FILES += \ + arch-mips/backtrace-mips.c \ + arch-mips/ptrace-mips.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) + +# Build test. +include $(CLEAR_VARS) +LOCAL_SRC_FILES := test.cpp +LOCAL_CFLAGS += -Werror -fno-inline-small-functions +LOCAL_SHARED_LIBRARIES := libcorkscrew +LOCAL_MODULE := libcorkscrew_test +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) + + +# TODO: reenable darwin-x86 +# ifeq ($(HOST_ARCH),x86) +ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86) + +# Build libcorkscrew. +include $(CLEAR_VARS) +LOCAL_SRC_FILES += $(generic_src_files) $(x86_src_files) +LOCAL_CFLAGS += -DCORKSCREW_HAVE_ARCH +LOCAL_STATIC_LIBRARIES += libcutils +LOCAL_LDLIBS += -ldl +ifeq ($(HOST_OS),linux) + LOCAL_SHARED_LIBRARIES += libgccdemangle # TODO: is this even needed on Linux? + LOCAL_LDLIBS += -lrt +endif +LOCAL_CFLAGS += -std=gnu99 -Werror +LOCAL_MODULE := libcorkscrew +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_SHARED_LIBRARY) + +# Build test. +include $(CLEAR_VARS) +LOCAL_SRC_FILES := test.cpp +LOCAL_CFLAGS += -Werror +LOCAL_SHARED_LIBRARIES := libcorkscrew +LOCAL_MODULE := libcorkscrew_test +LOCAL_MODULE_TAGS := optional +include $(BUILD_HOST_EXECUTABLE) + +endif # HOST_ARCH == x86 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..ff6c192 --- /dev/null +++ b/libcorkscrew/arch-arm/backtrace-arm.c @@ -0,0 +1,601 @@ +/* + * 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> + +#if !defined(__BIONIC_HAVE_UCONTEXT_T) +/* Old versions of the Android <signal.h> didn't define ucontext_t. */ +#include <asm/sigcontext.h> /* Ensure 'struct sigcontext' is defined. */ + +/* 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 uc_mcontext; + uint32_t uc_sigmask; +} ucontext_t; +#endif /* !__BIONIC_HAVE_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; + + state.gregs[0] = uc->uc_mcontext.arm_r0; + state.gregs[1] = uc->uc_mcontext.arm_r1; + state.gregs[2] = uc->uc_mcontext.arm_r2; + state.gregs[3] = uc->uc_mcontext.arm_r3; + state.gregs[4] = uc->uc_mcontext.arm_r4; + state.gregs[5] = uc->uc_mcontext.arm_r5; + state.gregs[6] = uc->uc_mcontext.arm_r6; + state.gregs[7] = uc->uc_mcontext.arm_r7; + state.gregs[8] = uc->uc_mcontext.arm_r8; + state.gregs[9] = uc->uc_mcontext.arm_r9; + state.gregs[10] = uc->uc_mcontext.arm_r10; + state.gregs[11] = uc->uc_mcontext.arm_fp; + state.gregs[12] = uc->uc_mcontext.arm_ip; + state.gregs[13] = uc->uc_mcontext.arm_sp; + state.gregs[14] = uc->uc_mcontext.arm_lr; + state.gregs[15] = uc->uc_mcontext.arm_pc; + + 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..78a9ea9 --- /dev/null +++ b/libcorkscrew/arch-arm/ptrace-arm.c @@ -0,0 +1,73 @@ +/* + * 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_ehsize; + uint32_t elf_shentsize_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_ehsize), + &elf_phentsize_ehsize) + && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), + &elf_shentsize_phnum)) { + uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; + uint32_t elf_phnum = elf_shentsize_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-mips/backtrace-mips.c b/libcorkscrew/arch-mips/backtrace-mips.c new file mode 100644 index 0000000..1abdd83 --- /dev/null +++ b/libcorkscrew/arch-mips/backtrace-mips.c @@ -0,0 +1,197 @@ +/* + * 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. + */ + +/* + * Backtracing functions for mips + */ + +#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> + +/* For PTRACE_GETREGS */ +typedef struct { + /* FIXME: check this definition */ + uint64_t regs[32]; + uint64_t lo; + uint64_t hi; + uint64_t epc; + uint64_t badvaddr; + uint64_t status; + uint64_t cause; +} user_regs_struct; + +/* Machine context at the time a signal was raised. */ +typedef struct ucontext { + /* FIXME: use correct definition */ + uint32_t sp; + uint32_t ra; + uint32_t pc; +} ucontext_t; + +/* Unwind state. */ +typedef struct { + uint32_t sp; + uint32_t ra; + uint32_t pc; +} unwind_state_t; + +uintptr_t rewind_pc_arch(const memory_t* memory, uintptr_t pc) { + if (pc == 0) + return pc; + if ((pc & 1) == 0) + return pc-8; /* jal/bal/jalr + branch delay slot */ + 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->pc) : state->pc; + backtrace_frame_t* frame; + uintptr_t addr; + int maxcheck = 1024; + int stack_size = 0, ra_offset = 0; + bool found_start = false; + + frame = add_backtrace_entry(pc, backtrace, ignore_depth, + max_depth, &ignored_frames, &returned_frames); + + if (frame) + frame->stack_top = state->sp; + + ALOGV("#%d: frame=%p pc=%08x sp=%08x\n", + index, frame, frame->absolute_pc, frame->stack_top); + + for (addr = state->pc; maxcheck-- > 0 && !found_start; addr -= 4) { + uint32_t op; + if (!try_get_word(memory, addr, &op)) + break; + + // ALOGV("@0x%08x: 0x%08x\n", addr, op); + switch (op & 0xffff0000) { + case 0x27bd0000: // addiu sp, imm + { + // looking for stack being decremented + int32_t immediate = ((((int)op) << 16) >> 16); + if (immediate < 0) { + stack_size = -immediate; + found_start = true; + ALOGV("@0x%08x: found stack adjustment=%d\n", addr, stack_size); + } + } + break; + case 0xafbf0000: // sw ra, imm(sp) + ra_offset = ((((int)op) << 16) >> 16); + ALOGV("@0x%08x: found ra offset=%d\n", addr, ra_offset); + break; + case 0x3c1c0000: // lui gp + ALOGV("@0x%08x: found function boundary\n", addr); + found_start = true; + break; + default: + break; + } + } + + if (ra_offset) { + uint32_t next_ra; + if (!try_get_word(memory, state->sp + ra_offset, &next_ra)) + break; + state->ra = next_ra; + ALOGV("New ra: 0x%08x\n", state->ra); + } + + if (stack_size) { + if (frame) + frame->stack_size = stack_size; + state->sp += stack_size; + ALOGV("New sp: 0x%08x\n", state->sp); + } + + if (state->pc == state->ra && stack_size == 0) + break; + + if (state->ra == 0) + break; + + state->pc = state->ra; + } + + ALOGV("returning %d frames\n", 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; + state.sp = uc->sp; + state.pc = uc->pc; + state.ra = uc->ra; + + ALOGV("unwind_backtrace_signal_arch: " + "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n", + ignore_depth, max_depth, state.pc, state.sp, state.ra); + + 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) { + + user_regs_struct regs; + if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + return -1; + } + + unwind_state_t state; + state.sp = regs.regs[29]; + state.ra = regs.regs[31]; + state.pc = regs.epc; + + ALOGV("unwind_backtrace_ptrace_arch: " + "ignore_depth=%d max_depth=%d pc=0x%08x sp=0x%08x ra=0x%08x\n", + ignore_depth, max_depth, state.pc, state.sp, state.ra); + + 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-mips/ptrace-mips.c index faf7b45..f0ea110 100644 --- a/nexus/SupplicantEvent.cpp +++ b/libcorkscrew/arch-mips/ptrace-mips.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/arch-x86/backtrace-x86.c b/libcorkscrew/arch-x86/backtrace-x86.c new file mode 100755 index 0000000..e133ab6 --- /dev/null +++ b/libcorkscrew/arch-x86/backtrace-x86.c @@ -0,0 +1,863 @@ +/* + * 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 "../ptrace-arch.h" +#include <corkscrew/ptrace.h> +#include "dwarf.h" + +#include <stdlib.h> +#include <signal.h> +#include <stdbool.h> +#include <limits.h> +#include <errno.h> +#include <string.h> +#include <sys/ptrace.h> +#include <cutils/log.h> + +#if defined(__BIONIC__) + +#if defined(__BIONIC_HAVE_UCONTEXT_T) + +// Bionic offers the Linux kernel headers. +#include <asm/sigcontext.h> +#include <asm/ucontext.h> +typedef struct ucontext ucontext_t; + +#else /* __BIONIC_HAVE_UCONTEXT_T */ + +/* Old versions of the Android <signal.h> didn't define ucontext_t. */ + +typedef struct { + uint32_t gregs[32]; + void* fpregs; + uint32_t oldmask; + uint32_t cr2; +} mcontext_t; + +enum { + REG_GS = 0, REG_FS, REG_ES, REG_DS, + REG_EDI, REG_ESI, REG_EBP, REG_ESP, + REG_EBX, REG_EDX, REG_ECX, REG_EAX, + REG_TRAPNO, REG_ERR, REG_EIP, REG_CS, + REG_EFL, REG_UESP, REG_SS +}; + +/* Machine context at the time a signal was raised. */ +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + uint32_t uc_sigmask; +} ucontext_t; + +#endif /* __BIONIC_HAVE_UCONTEXT_T */ + +#elif defined(__APPLE__) + +#define _XOPEN_SOURCE +#include <ucontext.h> + +#else + +// glibc has its own renaming of the Linux kernel's structures. +#define __USE_GNU // For REG_EBP, REG_ESP, and REG_EIP. +#include <ucontext.h> + +#endif + +/* Unwind state. */ +typedef struct { + uint32_t reg[DWARF_REGISTERS]; +} unwind_state_t; + +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; + +uintptr_t rewind_pc_arch(const memory_t* memory __attribute__((unused)), uintptr_t pc) { + /* TODO: x86 instructions are 1-16 bytes, to define exact size of previous instruction + we have to disassemble from the function entry point up to pc. + Returning pc-1 is probably enough for now, the only drawback is that + it points somewhere between the first byte of instruction we are looking for and + the first byte of the next instruction. */ + + return pc-1; + /* TODO: We should adjust that for the signal frames and return pc for them instead of pc-1. + To recognize signal frames we should read cie_info property. */ +} + +/* Read byte through 4 byte cache. Usually we read byte by byte and updating cursor. */ +static bool try_get_byte(const memory_t* memory, uintptr_t ptr, uint8_t* out_value, uint32_t* cursor) { + static uintptr_t lastptr; + static uint32_t buf; + + ptr += *cursor; + + if (ptr < lastptr || lastptr + 3 < ptr) { + lastptr = (ptr >> 2) << 2; + if (!try_get_word(memory, lastptr, &buf)) { + return false; + } + } + *out_value = (uint8_t)((buf >> ((ptr & 3) * 8)) & 0xff); + ++*cursor; + return true; +} + +/* Getting X bytes. 4 is maximum for now. */ +static bool try_get_xbytes(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t bytes, uint32_t* cursor) { + uint32_t data = 0; + if (bytes > 4) { + ALOGE("can't read more than 4 bytes, trying to read %d", bytes); + return false; + } + for (int i = 0; i < bytes; i++) { + uint8_t buf; + if (!try_get_byte(memory, ptr, &buf, cursor)) { + return false; + } + data |= (uint32_t)buf << (i * 8); + } + *out_value = data; + return true; +} + +/* Reads signed/unsigned LEB128 encoded data. From 1 to 4 bytes. */ +static bool try_get_leb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor, bool sign_extend) { + uint8_t buf = 0; + uint32_t val = 0; + uint8_t c = 0; + do { + if (!try_get_byte(memory, ptr, &buf, cursor)) { + return false; + } + val |= ((uint32_t)buf & 0x7f) << (c * 7); + c++; + } while (buf & 0x80 && (c * 7) <= 32); + if (c * 7 > 32) { + ALOGE("%s: data exceeds expected 4 bytes maximum", __FUNCTION__); + return false; + } + if (sign_extend) { + if (buf & 0x40) { + val |= ((uint32_t)-1 << (c * 7)); + } + } + *out_value = val; + return true; +} + +/* Reads signed LEB128 encoded data. From 1 to 4 bytes. */ +static bool try_get_sleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { + return try_get_leb128(memory, ptr, out_value, cursor, true); +} + +/* Reads unsigned LEB128 encoded data. From 1 to 4 bytes. */ +static bool try_get_uleb128(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint32_t* cursor) { + return try_get_leb128(memory, ptr, out_value, cursor, false); +} + +/* Getting data encoded by dwarf encodings. */ +static bool read_dwarf(const memory_t* memory, uintptr_t ptr, uint32_t* out_value, uint8_t encoding, uint32_t* cursor) { + uint32_t data = 0; + bool issigned = true; + uintptr_t addr = ptr + *cursor; + /* Lower 4 bits is data type/size */ + /* TODO: add more encodings if it becomes necessary */ + switch (encoding & 0xf) { + case DW_EH_PE_absptr: + if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { + return false; + } + *out_value = data; + return true; + case DW_EH_PE_udata4: + issigned = false; + case DW_EH_PE_sdata4: + if (!try_get_xbytes(memory, ptr, &data, 4, cursor)) { + return false; + } + break; + default: + ALOGE("unrecognized dwarf lower part encoding: 0x%x", encoding); + return false; + } + /* Higher 4 bits is modifier */ + /* TODO: add more encodings if it becomes necessary */ + switch (encoding & 0xf0) { + case 0: + *out_value = data; + break; + case DW_EH_PE_pcrel: + if (issigned) { + *out_value = addr + (int32_t)data; + } else { + *out_value = addr + data; + } + break; + /* Assuming ptr is correct base to calculate datarel */ + case DW_EH_PE_datarel: + if (issigned) { + *out_value = ptr + (int32_t)data; + } else { + *out_value = ptr + data; + } + break; + default: + ALOGE("unrecognized dwarf higher part encoding: 0x%x", encoding); + return false; + } + return true; +} + +/* Having PC find corresponding FDE by reading .eh_frame_hdr section data. */ +static uintptr_t find_fde(const memory_t* memory, + const map_info_t* map_info_list, uintptr_t pc) { + if (!pc) { + ALOGV("find_fde: pc is zero, no eh_frame"); + return 0; + } + const map_info_t* mi = find_map_info(map_info_list, pc); + if (!mi) { + ALOGV("find_fde: no map info for pc:0x%x", pc); + return 0; + } + const map_info_data_t* midata = mi->data; + if (!midata) { + ALOGV("find_fde: no eh_frame_hdr for map: start=0x%x, end=0x%x", mi->start, mi->end); + return 0; + } + + eh_frame_hdr_info_t eh_hdr_info; + memset(&eh_hdr_info, 0, sizeof(eh_frame_hdr_info_t)); + + /* Getting the first word of eh_frame_hdr: + 1st byte is version; + 2nd byte is encoding of pointer to eh_frames; + 3rd byte is encoding of count of FDEs in lookup table; + 4th byte is encoding of lookup table entries. + */ + uintptr_t eh_frame_hdr = midata->eh_frame_hdr; + uint32_t c = 0; + if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.version, &c)) return 0; + if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; + if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_count_enc, &c)) return 0; + if (!try_get_byte(memory, eh_frame_hdr, &eh_hdr_info.fde_table_enc, &c)) return 0; + + /* TODO: 3rd byte can be DW_EH_PE_omit, that means no lookup table available and we should + try to parse eh_frame instead. Not sure how often it may occur, skipping now. + */ + if (eh_hdr_info.version != 1) { + ALOGV("find_fde: eh_frame_hdr version %d is not supported", eh_hdr_info.version); + return 0; + } + /* Getting the data: + 2nd word is eh_frame pointer (normally not used, because lookup table has all we need); + 3rd word is count of FDEs in the lookup table; + starting from 4 word there is FDE lookup table (pairs of PC and FDE pointer) sorted by PC; + */ + if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.eh_frame_ptr, eh_hdr_info.eh_frame_ptr_enc, &c)) return 0; + if (!read_dwarf(memory, eh_frame_hdr, &eh_hdr_info.fde_count, eh_hdr_info.fde_count_enc, &c)) return 0; + ALOGV("find_fde: found %d FDEs", eh_hdr_info.fde_count); + + int32_t low = 0; + int32_t high = eh_hdr_info.fde_count; + uintptr_t start = 0; + uintptr_t fde = 0; + /* eh_frame_hdr + c points to lookup table at this point. */ + while (low <= high) { + uint32_t mid = (high + low)/2; + uint32_t entry = c + mid * 8; + if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &entry)) return 0; + if (pc <= start) { + high = mid - 1; + } else { + low = mid + 1; + } + } + /* Value found is at high. */ + if (high < 0) { + ALOGV("find_fde: pc %x is out of FDE bounds: %x", pc, start); + return 0; + } + c += high * 8; + if (!read_dwarf(memory, eh_frame_hdr, &start, eh_hdr_info.fde_table_enc, &c)) return 0; + if (!read_dwarf(memory, eh_frame_hdr, &fde, eh_hdr_info.fde_table_enc, &c)) return 0; + ALOGV("pc 0x%x, ENTRY %d: start=0x%x, fde=0x%x", pc, high, start, fde); + return fde; +} + +/* Execute single dwarf instruction and update dwarf state accordingly. */ +static bool execute_dwarf(const memory_t* memory, uintptr_t ptr, cie_info_t* cie_info, + dwarf_state_t* dstate, uint32_t* cursor, + dwarf_state_t* stack, uint8_t* stack_ptr) { + uint8_t inst; + uint8_t op = 0; + + if (!try_get_byte(memory, ptr, &inst, cursor)) { + return false; + } + ALOGV("DW_CFA inst: 0x%x", inst); + + /* For some instructions upper 2 bits is opcode and lower 6 bits is operand. See dwarf-2.0 7.23. */ + if (inst & 0xc0) { + op = inst & 0x3f; + inst &= 0xc0; + } + + switch ((dwarf_CFA)inst) { + uint32_t reg = 0; + uint32_t offset = 0; + case DW_CFA_advance_loc: + dstate->loc += op * cie_info->code_align; + ALOGV("DW_CFA_advance_loc: %d to 0x%x", op, dstate->loc); + break; + case DW_CFA_offset: + if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; + dstate->regs[op].rule = 'o'; + dstate->regs[op].value = offset * cie_info->data_align; + ALOGV("DW_CFA_offset: r%d = o(%d)", op, dstate->regs[op].value); + break; + case DW_CFA_restore: + dstate->regs[op].rule = stack->regs[op].rule; + dstate->regs[op].value = stack->regs[op].value; + ALOGV("DW_CFA_restore: r%d = %c(%d)", op, dstate->regs[op].rule, dstate->regs[op].value); + break; + case DW_CFA_nop: + break; + case DW_CFA_set_loc: // probably we don't have it on x86. + if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; + if (offset < dstate->loc) { + ALOGE("DW_CFA_set_loc: attempt to move location backward"); + return false; + } + dstate->loc = offset * cie_info->code_align; + ALOGV("DW_CFA_set_loc: %d to 0x%x", offset * cie_info->code_align, dstate->loc); + break; + case DW_CFA_advance_loc1: + if (!try_get_byte(memory, ptr, (uint8_t*)&offset, cursor)) return false; + dstate->loc += (uint8_t)offset * cie_info->code_align; + ALOGV("DW_CFA_advance_loc1: %d to 0x%x", (uint8_t)offset * cie_info->code_align, dstate->loc); + break; + case DW_CFA_advance_loc2: + if (!try_get_xbytes(memory, ptr, &offset, 2, cursor)) return false; + dstate->loc += (uint16_t)offset * cie_info->code_align; + ALOGV("DW_CFA_advance_loc2: %d to 0x%x", (uint16_t)offset * cie_info->code_align, dstate->loc); + break; + case DW_CFA_advance_loc4: + if (!try_get_xbytes(memory, ptr, &offset, 4, cursor)) return false; + dstate->loc += offset * cie_info->code_align; + ALOGV("DW_CFA_advance_loc4: %d to 0x%x", offset * cie_info->code_align, dstate->loc); + break; + case DW_CFA_offset_extended: // probably we don't have it on x86. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; + if (reg > DWARF_REGISTERS) { + ALOGE("DW_CFA_offset_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); + return false; + } + dstate->regs[reg].rule = 'o'; + dstate->regs[reg].value = offset * cie_info->data_align; + ALOGV("DW_CFA_offset_extended: r%d = o(%d)", reg, dstate->regs[reg].value); + break; + case DW_CFA_restore_extended: // probably we don't have it on x86. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + dstate->regs[reg].rule = stack->regs[reg].rule; + dstate->regs[reg].value = stack->regs[reg].value; + if (reg > DWARF_REGISTERS) { + ALOGE("DW_CFA_restore_extended: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); + return false; + } + ALOGV("DW_CFA_restore: r%d = %c(%d)", reg, dstate->regs[reg].rule, dstate->regs[reg].value); + break; + case DW_CFA_undefined: // probably we don't have it on x86. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + dstate->regs[reg].rule = 'u'; + dstate->regs[reg].value = 0; + if (reg > DWARF_REGISTERS) { + ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); + return false; + } + ALOGV("DW_CFA_undefined: r%d", reg); + break; + case DW_CFA_same_value: // probably we don't have it on x86. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + dstate->regs[reg].rule = 's'; + dstate->regs[reg].value = 0; + if (reg > DWARF_REGISTERS) { + ALOGE("DW_CFA_undefined: r%d exceeds supported number of registers (%d)", reg, DWARF_REGISTERS); + return false; + } + ALOGV("DW_CFA_same_value: r%d", reg); + break; + case DW_CFA_register: // probably we don't have it on x86. + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + /* that's new register actually, not offset */ + if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; + if (reg > DWARF_REGISTERS || offset > DWARF_REGISTERS) { + ALOGE("DW_CFA_register: r%d or r%d exceeds supported number of registers (%d)", reg, offset, DWARF_REGISTERS); + return false; + } + dstate->regs[reg].rule = 'r'; + dstate->regs[reg].value = offset; + ALOGV("DW_CFA_register: r%d = r(%d)", reg, dstate->regs[reg].value); + break; + case DW_CFA_remember_state: + if (*stack_ptr == DWARF_STATES_STACK) { + ALOGE("DW_CFA_remember_state: states stack overflow %d", *stack_ptr); + return false; + } + stack[(*stack_ptr)++] = *dstate; + ALOGV("DW_CFA_remember_state: stacktop moves to %d", *stack_ptr); + break; + case DW_CFA_restore_state: + /* We have CIE state saved at 0 position. It's not supposed to be taken + by DW_CFA_restore_state. */ + if (*stack_ptr == 1) { + ALOGE("DW_CFA_restore_state: states stack is empty"); + return false; + } + /* Don't touch location on restore. */ + uintptr_t saveloc = dstate->loc; + *dstate = stack[--*stack_ptr]; + dstate->loc = saveloc; + ALOGV("DW_CFA_restore_state: stacktop moves to %d", *stack_ptr); + break; + case DW_CFA_def_cfa: + if (!try_get_uleb128(memory, ptr, ®, cursor)) return false; + if (!try_get_uleb128(memory, ptr, &offset, cursor)) return false; + dstate->cfa_reg = reg; + dstate->cfa_off = offset; + ALOGV("DW_CFA_def_cfa: %x(r%d)", offset, reg); + break; + case DW_CFA_def_cfa_register: + if (!try_get_uleb128(memory, ptr, ®, cursor)) { + return false; + } + dstate->cfa_reg = reg; + ALOGV("DW_CFA_def_cfa_register: r%d", reg); + break; + case DW_CFA_def_cfa_offset: + if (!try_get_uleb128(memory, ptr, &offset, cursor)) { + return false; + } + dstate->cfa_off = offset; + ALOGV("DW_CFA_def_cfa_offset: %x", offset); + break; + default: + ALOGE("unrecognized DW_CFA_* instruction: 0x%x", inst); + return false; + } + return true; +} + +/* Restoring particular register value based on dwarf state. */ +static bool get_old_register_value(const memory_t* memory, uint32_t cfa, + dwarf_state_t* dstate, uint8_t reg, + unwind_state_t* state, unwind_state_t* newstate) { + uint32_t addr; + switch (dstate->regs[reg].rule) { + case 0: + /* We don't have dstate updated for this register, so assuming value kept the same. + Normally we should look into state and return current value as the old one + but we don't have all registers in state to handle this properly */ + ALOGV("get_old_register_value: value of r%d is the same", reg); + // for ESP if it's not updated by dwarf rule we assume it's equal to CFA + if (reg == DWARF_ESP) { + ALOGV("get_old_register_value: adjusting esp to CFA: 0x%x", cfa); + newstate->reg[reg] = cfa; + } else { + newstate->reg[reg] = state->reg[reg]; + } + break; + case 'o': + addr = cfa + (int32_t)dstate->regs[reg].value; + if (!try_get_word(memory, addr, &newstate->reg[reg])) { + ALOGE("get_old_register_value: can't read from 0x%x", addr); + return false; + } + ALOGV("get_old_register_value: r%d at 0x%x is 0x%x", reg, addr, newstate->reg[reg]); + break; + case 'r': + /* We don't have all registers in state so don't even try to look at 'r' */ + ALOGE("get_old_register_value: register lookup not implemented yet"); + break; + default: + ALOGE("get_old_register_value: unexpected rule:%c value:%d for register %d", + dstate->regs[reg].rule, (int32_t)dstate->regs[reg].value, reg); + return false; + } + return true; +} + +/* Updaing state based on dwarf state. */ +static bool update_state(const memory_t* memory, unwind_state_t* state, + dwarf_state_t* dstate, cie_info_t* cie_info) { + unwind_state_t newstate; + /* We can restore more registers here if we need them. Meanwile doing minimal work here. */ + /* Getting CFA. */ + uintptr_t cfa = 0; + if (dstate->cfa_reg == DWARF_ESP) { + cfa = state->reg[DWARF_ESP] + dstate->cfa_off; + } else if (dstate->cfa_reg == DWARF_EBP) { + cfa = state->reg[DWARF_EBP] + dstate->cfa_off; + } else { + ALOGE("update_state: unexpected CFA register: %d", dstate->cfa_reg); + return false; + } + ALOGV("update_state: new CFA: 0x%x", cfa); + /* Getting EIP. */ + if (!get_old_register_value(memory, cfa, dstate, DWARF_EIP, state, &newstate)) return false; + /* Getting EBP. */ + if (!get_old_register_value(memory, cfa, dstate, DWARF_EBP, state, &newstate)) return false; + /* Getting ESP. */ + if (!get_old_register_value(memory, cfa, dstate, DWARF_ESP, state, &newstate)) return false; + + ALOGV("update_state: IP: 0x%x; restore IP: 0x%x", state->reg[DWARF_EIP], newstate.reg[DWARF_EIP]); + ALOGV("update_state: EBP: 0x%x; restore EBP: 0x%x", state->reg[DWARF_EBP], newstate.reg[DWARF_EBP]); + ALOGV("update_state: ESP: 0x%x; restore ESP: 0x%x", state->reg[DWARF_ESP], newstate.reg[DWARF_ESP]); + *state = newstate; + return true; +} + +/* Execute CIE and FDE instructions for FDE found with find_fde. */ +static bool execute_fde(const memory_t* memory, + const map_info_t* map_info_list, + uintptr_t fde, + unwind_state_t* state) { + uint32_t fde_length = 0; + uint32_t cie_length = 0; + uintptr_t cie = 0; + uintptr_t cie_offset = 0; + cie_info_t cie_i; + cie_info_t* cie_info = &cie_i; + fde_info_t fde_i; + fde_info_t* fde_info = &fde_i; + dwarf_state_t dwarf_state; + dwarf_state_t* dstate = &dwarf_state; + dwarf_state_t stack[DWARF_STATES_STACK]; + uint8_t stack_ptr = 0; + + memset(dstate, 0, sizeof(dwarf_state_t)); + memset(cie_info, 0, sizeof(cie_info_t)); + memset(fde_info, 0, sizeof(fde_info_t)); + + /* Read common CIE or FDE area: + 1st word is length; + 2nd word is ID: 0 for CIE, CIE pointer for FDE. + */ + if (!try_get_word(memory, fde, &fde_length)) { + return false; + } + if ((int32_t)fde_length == -1) { + ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); + return false; + } + if (!try_get_word(memory, fde + 4, &cie_offset)) { + return false; + } + if (cie_offset == 0) { + /* This is CIE. We shouldn't be here normally. */ + cie = fde; + cie_length = fde_length; + } else { + /* Find CIE. */ + /* Positive cie_offset goes backward from current field. */ + cie = fde + 4 - cie_offset; + if (!try_get_word(memory, cie, &cie_length)) { + return false; + } + if ((int32_t)cie_length == -1) { + ALOGV("execute_fde: 64-bit dwarf detected, not implemented yet"); + return false; + } + if (!try_get_word(memory, cie + 4, &cie_offset)) { + return false; + } + if (cie_offset != 0) { + ALOGV("execute_fde: can't find CIE"); + return false; + } + } + ALOGV("execute_fde: FDE length: %d", fde_length); + ALOGV("execute_fde: CIE pointer: %x", cie); + ALOGV("execute_fde: CIE length: %d", cie_length); + + /* Read CIE: + Augmentation independent: + 1st byte is version; + next x bytes is /0 terminated augmentation string; + next x bytes is unsigned LEB128 encoded code alignment factor; + next x bytes is signed LEB128 encoded data alignment factor; + next 1 (CIE version 1) or x (CIE version 3 unsigned LEB128) bytes is return register column; + Augmentation dependent: + if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; + if 'L' next 1 byte is LSDA encoding; + if 'R' next 1 byte is FDE encoding; + if 'S' CIE represents signal handler stack frame; + if 'P' next 1 byte is personality encoding folowed by personality function pointer; + Next x bytes is CIE program. + */ + + uint32_t c = 8; + if (!try_get_byte(memory, cie, &cie_info->version, &c)) { + return false; + } + ALOGV("execute_fde: CIE version: %d", cie_info->version); + uint8_t ch; + do { + if (!try_get_byte(memory, cie, &ch, &c)) { + return false; + } + switch (ch) { + case '\0': break; + case 'z': cie_info->aug_z = 1; break; + case 'L': cie_info->aug_L = 1; break; + case 'R': cie_info->aug_R = 1; break; + case 'S': cie_info->aug_S = 1; break; + case 'P': cie_info->aug_P = 1; break; + default: + ALOGV("execute_fde: Unrecognized CIE augmentation char: '%c'", ch); + return false; + break; + } + } while (ch); + if (!try_get_uleb128(memory, cie, &cie_info->code_align, &c)) { + return false; + } + if (!try_get_sleb128(memory, cie, &cie_info->data_align, &c)) { + return false; + } + if (cie_info->version >= 3) { + if (!try_get_uleb128(memory, cie, &cie_info->reg, &c)) { + return false; + } + } else { + if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->reg, &c)) { + return false; + } + } + ALOGV("execute_fde: CIE code alignment factor: %d", cie_info->code_align); + ALOGV("execute_fde: CIE data alignment factor: %d", cie_info->data_align); + if (cie_info->aug_z) { + if (!try_get_uleb128(memory, cie, &cie_info->aug_z, &c)) { + return false; + } + } + if (cie_info->aug_L) { + if (!try_get_byte(memory, cie, &cie_info->aug_L, &c)) { + return false; + } + } else { + /* Default encoding. */ + cie_info->aug_L = DW_EH_PE_absptr; + } + if (cie_info->aug_R) { + if (!try_get_byte(memory, cie, &cie_info->aug_R, &c)) { + return false; + } + } else { + /* Default encoding. */ + cie_info->aug_R = DW_EH_PE_absptr; + } + if (cie_info->aug_P) { + /* Get encoding of personality routine pointer. We don't use it now. */ + if (!try_get_byte(memory, cie, (uint8_t*)&cie_info->aug_P, &c)) { + return false; + } + /* Get routine pointer. */ + if (!read_dwarf(memory, cie, &cie_info->aug_P, (uint8_t)cie_info->aug_P, &c)) { + return false; + } + } + /* CIE program. */ + /* Length field itself (4 bytes) is not included into length. */ + stack[0] = *dstate; + stack_ptr = 1; + while (c < cie_length + 4) { + if (!execute_dwarf(memory, cie, cie_info, dstate, &c, stack, &stack_ptr)) { + return false; + } + } + + /* We went directly to CIE. Normally it shouldn't occur. */ + if (cie == fde) return true; + + /* Go back to FDE. */ + c = 8; + /* Read FDE: + Augmentation independent: + next x bytes (encoded as specified in CIE) is FDE starting address; + next x bytes (encoded as specified in CIE) is FDE number of instructions covered; + Augmentation dependent: + if 'z' next x bytes is unsigned LEB128 encoded augmentation data size; + if 'L' next x bytes is LSDA pointer (encoded as specified in CIE); + Next x bytes is FDE program. + */ + if (!read_dwarf(memory, fde, &fde_info->start, (uint8_t)cie_info->aug_R, &c)) { + return false; + } + dstate->loc = fde_info->start; + ALOGV("execute_fde: FDE start: %x", dstate->loc); + if (!read_dwarf(memory, fde, &fde_info->length, 0, &c)) { + return false; + } + ALOGV("execute_fde: FDE length: %x", fde_info->length); + if (cie_info->aug_z) { + if (!try_get_uleb128(memory, fde, &fde_info->aug_z, &c)) { + return false; + } + } + if (cie_info->aug_L && cie_info->aug_L != DW_EH_PE_omit) { + if (!read_dwarf(memory, fde, &fde_info->aug_L, cie_info->aug_L, &c)) { + return false; + } + } + /* FDE program. */ + /* Length field itself (4 bytes) is not included into length. */ + /* Save CIE state as 0 element of stack. Used by DW_CFA_restore. */ + stack[0] = *dstate; + stack_ptr = 1; + while (c < fde_length + 4 && state->reg[DWARF_EIP] >= dstate->loc) { + if (!execute_dwarf(memory, fde, cie_info, dstate, &c, stack, &stack_ptr)) { + return false; + } + ALOGV("IP: %x, LOC: %x", state->reg[DWARF_EIP], dstate->loc); + } + + return update_state(memory, state, dstate, cie_info); +} + +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; + + ALOGV("Unwinding tid: %d", memory->tid); + ALOGV("IP: %x", state->reg[DWARF_EIP]); + ALOGV("BP: %x", state->reg[DWARF_EBP]); + ALOGV("SP: %x", state->reg[DWARF_ESP]); + + for (size_t index = 0; returned_frames < max_depth; index++) { + uintptr_t fde = find_fde(memory, map_info_list, state->reg[DWARF_EIP]); + /* FDE is not found, it may happen if stack is corrupted or calling wrong adress. + Getting return address from stack. + */ + if (!fde) { + uint32_t ip; + ALOGV("trying to restore registers from stack"); + if (!try_get_word(memory, state->reg[DWARF_EBP] + 4, &ip) || + ip == state->reg[DWARF_EIP]) { + ALOGV("can't get IP from stack"); + break; + } + /* We've been able to get IP from stack so recording the frame before continue. */ + backtrace_frame_t* frame = add_backtrace_entry( + index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP], + backtrace, ignore_depth, max_depth, + &ignored_frames, &returned_frames); + state->reg[DWARF_EIP] = ip; + state->reg[DWARF_ESP] = state->reg[DWARF_EBP] + 8; + if (!try_get_word(memory, state->reg[DWARF_EBP], &state->reg[DWARF_EBP])) { + ALOGV("can't get EBP from stack"); + break; + } + ALOGV("restore IP: %x", state->reg[DWARF_EIP]); + ALOGV("restore BP: %x", state->reg[DWARF_EBP]); + ALOGV("restore SP: %x", state->reg[DWARF_ESP]); + continue; + } + backtrace_frame_t* frame = add_backtrace_entry( + index ? rewind_pc_arch(memory, state->reg[DWARF_EIP]) : state->reg[DWARF_EIP], + backtrace, ignore_depth, max_depth, + &ignored_frames, &returned_frames); + + uint32_t stack_top = state->reg[DWARF_ESP]; + + if (!execute_fde(memory, map_info_list, fde, state)) break; + + if (frame) { + frame->stack_top = stack_top; + if (stack_top < state->reg[DWARF_ESP]) { + frame->stack_size = state->reg[DWARF_ESP] - stack_top; + } + } + ALOGV("Stack: 0x%x ... 0x%x - %d bytes", frame->stack_top, state->reg[DWARF_ESP], frame->stack_size); + } + return returned_frames; +} + +ssize_t unwind_backtrace_signal_arch(siginfo_t* siginfo __attribute__((unused)), 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; +#if defined(__APPLE__) + state.reg[DWARF_EBP] = uc->uc_mcontext->__ss.__ebp; + state.reg[DWARF_ESP] = uc->uc_mcontext->__ss.__esp; + state.reg[DWARF_EIP] = uc->uc_mcontext->__ss.__eip; +#else + state.reg[DWARF_EBP] = uc->uc_mcontext.gregs[REG_EBP]; + state.reg[DWARF_ESP] = uc->uc_mcontext.gregs[REG_ESP]; + state.reg[DWARF_EIP] = uc->uc_mcontext.gregs[REG_EIP]; +#endif + + 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) { +#if defined(__APPLE__) + return -1; +#else + pt_regs_x86_t regs; + if (ptrace(PTRACE_GETREGS, tid, 0, ®s)) { + return -1; + } + + unwind_state_t state; + state.reg[DWARF_EBP] = regs.ebp; + state.reg[DWARF_EIP] = regs.eip; + state.reg[DWARF_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); +#endif +} diff --git a/libcorkscrew/arch-x86/dwarf.h b/libcorkscrew/arch-x86/dwarf.h new file mode 100755 index 0000000..962fc55 --- /dev/null +++ b/libcorkscrew/arch-x86/dwarf.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 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. + */ + +/* + * Dwarf2 data encoding flags. + */ + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 +#define DW_EH_PE_indirect 0x80 + +/* + * Dwarf2 call frame instructions. + */ + +typedef enum { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e +} dwarf_CFA; + +/* + * eh_frame_hdr information. +*/ + +typedef struct { + uint8_t version; + uint8_t eh_frame_ptr_enc; + uint8_t fde_count_enc; + uint8_t fde_table_enc; + uintptr_t eh_frame_ptr; + uint32_t fde_count; +} eh_frame_hdr_info_t; + +/* + * CIE information. +*/ + +typedef struct { + uint8_t version; + uint32_t code_align; + uint32_t data_align; + uint32_t reg; + uint32_t aug_z; + uint8_t aug_L; + uint8_t aug_R; + uint8_t aug_S; + uint32_t aug_P; +} cie_info_t; + +/* + * FDE information. +*/ + +typedef struct { + uint32_t start; + uint32_t length; // number of instructions covered by FDE + uint32_t aug_z; + uint32_t aug_L; +} fde_info_t; + +/* + * Dwarf state. +*/ + +/* Stack of states: required for DW_CFA_remember_state/DW_CFA_restore_state + 30 should be enough */ +#define DWARF_STATES_STACK 30 + +typedef struct { + char rule; // rule: o - offset(value); r - register(value) + uint32_t value; // value +} reg_rule_t; + +/* Dwarf preserved number of registers for x86. */ + +#define DWARF_REGISTERS 17 + +typedef struct { + uintptr_t loc; // location (ip) + uint8_t cfa_reg; // index of register where CFA location stored + intptr_t cfa_off; // offset + reg_rule_t regs[DWARF_REGISTERS]; // dwarf preserved registers for x86 +} dwarf_state_t; + +/* DWARF registers we are caring about. */ + +#define DWARF_EAX 0 +#define DWARF_ECX 1 +#define DWARF_EDX 2 +#define DWARF_EBX 3 +#define DWARF_ESP 4 +#define DWARF_EBP 5 +#define DWARF_ESI 6 +#define DWARF_EDI 7 +#define DWARF_EIP 8 + + diff --git a/libcorkscrew/arch-x86/ptrace-x86.c b/libcorkscrew/arch-x86/ptrace-x86.c new file mode 100755 index 0000000..9c49b93 --- /dev/null +++ b/libcorkscrew/arch-x86/ptrace-x86.c @@ -0,0 +1,64 @@ +/* + * 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 <stddef.h> +#include <elf.h> +#include <cutils/log.h> + +static void load_eh_frame_hdr(pid_t pid, map_info_t* mi, uintptr_t *eh_frame_hdr) { + uint32_t elf_phoff; + uint32_t elf_phentsize_ehsize; + uint32_t elf_shentsize_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_ehsize), + &elf_phentsize_ehsize) + && try_get_word_ptrace(pid, mi->start + offsetof(Elf32_Ehdr, e_phnum), + &elf_shentsize_phnum)) { + uint32_t elf_phentsize = elf_phentsize_ehsize >> 16; + uint32_t elf_phnum = elf_shentsize_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_GNU_EH_FRAME) { + uint32_t elf_phdr_offset; + if (!try_get_word_ptrace(pid, elf_phdr + offsetof(Elf32_Phdr, p_offset), + &elf_phdr_offset)) { + break; + } + *eh_frame_hdr = mi->start + elf_phdr_offset; + ALOGV("Parsed .eh_frame_hdr info for %s: start=0x%08x", mi->name, *eh_frame_hdr); + return; + } + } + } + *eh_frame_hdr = 0; +} + +void load_ptrace_map_info_data_arch(pid_t pid, map_info_t* mi, map_info_data_t* data) { + load_eh_frame_hdr(pid, mi, &data->eh_frame_hdr); +} + +void free_ptrace_map_info_data_arch(map_info_t* mi __attribute__((unused)), + map_info_data_t* data __attribute__((unused))) { +} 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..b365e5b --- /dev/null +++ b/libcorkscrew/backtrace.c @@ -0,0 +1,334 @@ +/* + * 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 <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <unwind.h> +#include <cutils/log.h> +#include <cutils/atomic.h> + +#define __USE_GNU // For dladdr(3) in glibc. +#include <dlfcn.h> + +#if defined(__BIONIC__) + +// Bionic implements and exports gettid but only implements tgkill. +extern int tgkill(int tgid, int tid, int sig); + +#elif defined(__APPLE__) + +#include <sys/syscall.h> + +// Mac OS >= 10.6 has a system call equivalent to Linux's gettid(). +static pid_t gettid() { + return syscall(SYS_thread_selfid); +} + +#else + +// glibc doesn't implement or export either gettid or tgkill. + +#include <unistd.h> +#include <sys/syscall.h> + +static pid_t gettid() { + return syscall(__NR_gettid); +} + +static int tgkill(int tgid, int tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); +} + +#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 __attribute__((unused)), 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 + +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()); + + // TODO: there's no tgkill(2) on Mac OS, so we'd either need the + // mach_port_t or the pthread_t rather than the tid. +#if defined(CORKSCREW_HAVE_ARCH) && !defined(__APPLE__) + 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); + } + 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); + } + } + } + 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 __attribute__((unused)), + 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; + int fieldWidth = (bufferSize - 80) / 2; + if (symbolName) { + uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; + if (pc_offset) { + snprintf(buffer, bufferSize, "#%02u pc %p %.*s (%.*s+%u)", + frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName, + fieldWidth, symbolName, pc_offset); + } else { + snprintf(buffer, bufferSize, "#%02u pc %p %.*s (%.*s)", + frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName, + fieldWidth, symbolName); + } + } else { + snprintf(buffer, bufferSize, "#%02u pc %p %.*s", + frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName); + } +} diff --git a/libcorkscrew/demangle.c b/libcorkscrew/demangle.c new file mode 100644 index 0000000..30ab1b0 --- /dev/null +++ b/libcorkscrew/demangle.c @@ -0,0 +1,36 @@ +/* + * 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/demangle.h> + +#include <cutils/log.h> + +extern char *__cxa_demangle (const char *mangled, char *buf, size_t *len, + int *status); + +char* demangle_symbol_name(const char* name) { +#if defined(__APPLE__) + // Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7. + if (name != NULL && name[0] != '_') { + return NULL; + } +#endif + // __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..93dffbf --- /dev/null +++ b/libcorkscrew/map_info.c @@ -0,0 +1,279 @@ +/* + * 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 <stdlib.h> +#include <string.h> +#include <limits.h> +#include <pthread.h> +#include <unistd.h> +#include <cutils/log.h> +#include <sys/time.h> + +#if defined(__APPLE__) + +// Mac OS vmmap(1) output: +// __TEXT 0009f000-000a1000 [ 8K 8K] r-x/rwx SM=COW /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n +// 012345678901234567890123456789012345678901234567890123456789 +// 0 1 2 3 4 5 +static map_info_t* parse_vmmap_line(const char* line) { + unsigned long int start; + unsigned long int end; + char permissions[4]; + int name_pos; + if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c %n", + &start, &end, permissions, &name_pos) != 3) { + return NULL; + } + + const char* name = line + name_pos; + size_t name_len = strlen(name); + + map_info_t* mi = calloc(1, sizeof(map_info_t) + name_len); + if (mi != NULL) { + mi->start = start; + mi->end = end; + mi->is_readable = permissions[0] == 'r'; + mi->is_writable = permissions[1] == 'w'; + mi->is_executable = permissions[2] == 'x'; + mi->data = NULL; + memcpy(mi->name, name, name_len); + mi->name[name_len - 1] = '\0'; + ALOGV("Parsed map: start=0x%08x, end=0x%08x, " + "is_readable=%d, is_writable=%d is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, mi->is_executable, mi->name); + } + return mi; +} + +map_info_t* load_map_info_list(pid_t pid) { + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid); + FILE* fp = popen(cmd, "r"); + if (fp == NULL) { + return NULL; + } + + char line[1024]; + map_info_t* milist = NULL; + while (fgets(line, sizeof(line), fp) != NULL) { + map_info_t* mi = parse_vmmap_line(line); + if (mi != NULL) { + mi->next = milist; + milist = mi; + } + } + pclose(fp); + return milist; +} + +#else + +// Linux /proc/<pid>/maps lines: +// 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_writable = strlen(permissions) == 4 && permissions[1] == 'w'; + 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_writable=%d, is_executable=%d, name=%s", + mi->start, mi->end, + mi->is_readable, mi->is_writable, 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; +} + +#endif + +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_writable_map(const map_info_t* milist, uintptr_t addr) { + const map_info_t* mi = find_map_info(milist, addr); + return mi && mi->is_writable; +} + +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_ns() { +#if defined(HAVE_POSIX_CLOCKS) + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(CLOCK_MONOTONIC, &t); + return t.tv_sec * 1000000000LL + t.tv_nsec; +#else + struct timeval t; + gettimeofday(&t, NULL); + return t.tv_sec * 1000000000LL + t.tv_usec * 1000LL; +#endif +} + +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_ns(); + if (g_my_map_info_list != NULL) { + 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 == NULL) { + 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 != NULL) { + 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); + } +} + +void flush_my_map_info_list() { + pthread_mutex_lock(&g_my_map_info_list_mutex); + + if (g_my_map_info_list != NULL) { + my_map_info_data_t* data = (my_map_info_data_t*) g_my_map_info_list->data; + dec_ref(g_my_map_info_list, data); + g_my_map_info_list = NULL; + } + + pthread_mutex_unlock(&g_my_map_info_list_mutex); +} diff --git a/libcorkscrew/ptrace-arch.h b/libcorkscrew/ptrace-arch.h new file mode 100755 index 0000000..4451c29 --- /dev/null +++ b/libcorkscrew/ptrace-arch.h @@ -0,0 +1,49 @@ +/* + * 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; +#elif __i386__ + uintptr_t eh_frame_hdr; +#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..be58f7f --- /dev/null +++ b/libcorkscrew/ptrace.c @@ -0,0 +1,152 @@ +/* + * 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 <stdlib.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 %p", (void*) ptr); + if (ptr & 3) { + ALOGV("try_get_word: invalid pointer %p", (void*) ptr); + *out_value = 0xffffffffL; + return false; + } + if (memory->tid < 0) { + if (!is_readable_map(memory->map_info_list, ptr)) { + ALOGV("try_get_word: pointer %p not in a readable map", (void*) ptr); + *out_value = 0xffffffffL; + return false; + } + *out_value = *(uint32_t*)ptr; + return true; + } else { +#if defined(__APPLE__) + ALOGV("no ptrace on Mac OS"); + return false; +#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; +#endif + } +} + +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); + free(context); +} + +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..982ccc8 --- /dev/null +++ b/libcorkscrew/symbol_table.c @@ -0,0 +1,227 @@ +/* + * 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 <stdbool.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <cutils/log.h> + +#if defined(__APPLE__) +#else + +#include <elf.h> + +static bool is_elf(Elf32_Ehdr* e) { + return (e->e_ident[EI_MAG0] == ELFMAG0 && + e->e_ident[EI_MAG1] == ELFMAG1 && + e->e_ident[EI_MAG2] == ELFMAG2 && + e->e_ident[EI_MAG3] == ELFMAG3); +} + +#endif + +// 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; +#if !defined(__APPLE__) + 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); +#endif + +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/libcorkscrew/test.cpp b/libcorkscrew/test.cpp new file mode 100644 index 0000000..22dfa7d --- /dev/null +++ b/libcorkscrew/test.cpp @@ -0,0 +1,76 @@ +#include <corkscrew/backtrace.h> +#include <corkscrew/symbol_table.h> +#include <stdio.h> +#include <stdlib.h> + +int do_backtrace(float /* just to test demangling */) { + const size_t MAX_DEPTH = 32; + backtrace_frame_t* frames = (backtrace_frame_t*) malloc(sizeof(backtrace_frame_t) * MAX_DEPTH); + ssize_t frame_count = unwind_backtrace(frames, 0, MAX_DEPTH); + fprintf(stderr, "frame_count=%d\n", (int) frame_count); + if (frame_count <= 0) { + return frame_count; + } + + backtrace_symbol_t* backtrace_symbols = (backtrace_symbol_t*) malloc(sizeof(backtrace_symbol_t) * frame_count); + get_backtrace_symbols(frames, frame_count, backtrace_symbols); + + for (size_t i = 0; i < (size_t) frame_count; ++i) { + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &frames[i], &backtrace_symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + if (backtrace_symbols[i].symbol_name != NULL) { + // get_backtrace_symbols found the symbol's name with dladdr(3). + fprintf(stderr, " %s\n", line); + } else { + // We don't have a symbol. Maybe this is a static symbol, and + // we can look it up? + symbol_table_t* symbols = NULL; + if (backtrace_symbols[i].map_name != NULL) { + symbols = load_symbol_table(backtrace_symbols[i].map_name); + } + const symbol_t* symbol = NULL; + if (symbols != NULL) { + symbol = find_symbol(symbols, frames[i].absolute_pc); + } + if (symbol != NULL) { + int offset = frames[i].absolute_pc - symbol->start; + fprintf(stderr, " %s (%s%+d)\n", line, symbol->name, offset); + } else { + fprintf(stderr, " %s (\?\?\?)\n", line); + } + free_symbol_table(symbols); + } + } + + free_backtrace_symbols(backtrace_symbols, frame_count); + free(backtrace_symbols); + free(frames); + return frame_count; +} + +struct C { + int g(int i); +}; + +__attribute__ ((noinline)) int C::g(int i) { + if (i == 0) { + return do_backtrace(0.1); + } + return g(i - 1); +} + +extern "C" __attribute__ ((noinline)) int f() { + C c; + return c.g(5); +} + +int main() { + flush_my_map_info_list(); + f(); + + flush_my_map_info_list(); + f(); + + return 0; +} diff --git a/libcutils/Android.mk b/libcutils/Android.mk index b83ce68..17b320f 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -46,10 +46,11 @@ commonSources := \ record_stream.c \ process_name.c \ properties.c \ + qsort_r_compat.c \ threads.c \ sched_policy.c \ iosched_policy.c \ - str_parms.c + str_parms.c \ commonHostSources := \ ashmem-host.c @@ -74,13 +75,10 @@ ifeq ($(WINDOWS_HOST_ONLY),1) else commonSources += \ abort_socket.c \ - mspace.c \ + fs.c \ selector.c \ - tztime.c \ + multiuser.c \ zygote.c - - commonHostSources += \ - tzstrftime.c endif @@ -107,26 +105,40 @@ 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 else # !arm -ifeq ($(TARGET_ARCH),sh) -LOCAL_SRC_FILES += memory.c atomic-android-sh.c -else # !sh ifeq ($(TARGET_ARCH_VARIANT),x86-atom) LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c else # !x86-atom +ifeq ($(TARGET_ARCH),mips) +LOCAL_SRC_FILES += arch-mips/android_memset.c +else # !mips LOCAL_SRC_FILES += memory.c +endif # !mips 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 +148,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) @@ -145,3 +158,5 @@ LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/nexus/SupplicantConnectionTimeoutEvent.h b/libcutils/arch-mips/android_memset.c index 0d2606d..bbc99fe 100644 --- a/nexus/SupplicantConnectionTimeoutEvent.h +++ b/libcutils/arch-mips/android_memset.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,21 +14,18 @@ * limitations under the License. */ -#ifndef _SupplicantConnectionTimeoutEvent_H -#define _SupplicantConnectionTimeoutEvent_H +#include <cutils/memory.h> -#include "SupplicantEvent.h" +/* Use mips-assembler versions supplied by bionic/libc/arch-mips/string/memset.S: */ +void _memset16(uint16_t* dst, uint16_t value, size_t size); +void _memset32(uint32_t* dst, uint32_t value, size_t size); -class SupplicantConnectionTimeoutEvent : public SupplicantEvent { -private: - char *mBssid; - bool mReassociated; +void android_memset16(uint16_t* dst, uint16_t value, size_t size) +{ + _memset16(dst, value, size); +} -public: - SupplicantConnectionTimeoutEvent(int level, char *event, size_t len); - virtual ~SupplicantConnectionTimeoutEvent(); - - const char *getBssid() { return mBssid; } -}; - -#endif +void android_memset32(uint32_t* dst, uint32_t value, size_t size) +{ + _memset32(dst, value, size); +} diff --git a/libcutils/atomic-android-sh.c b/libcutils/atomic-android-sh.c deleted file mode 100644 index 8bac68a..0000000 --- a/libcutils/atomic-android-sh.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2007 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/atomic.h> -#ifdef HAVE_WIN32_THREADS -#include <windows.h> -#else -#include <sched.h> -#endif - -/* - * Note : - * - * (1) SuperH does not have CMPXCHG. It has only TAS for atomic - * operations. It does not seem a good idea to implement CMPXCHG, - * with TAS. So, we choose to implemnt these operations with - * posix mutexes. Please be sure that this might cause performance - * problem for Android-SH. Using LL/SC instructions supported in SH-X3, - * best performnace would be realized. - * - * (2) Mutex initialization problem happens, which is commented for - * ARM implementation, in this file above. - * We follow the fact that the initializer for mutex is a simple zero - * value. - * - * (3) These operations are NOT safe for SMP, as there is no currently - * no definition for a memory barrier operation. - */ - -#include <pthread.h> - -#define SWAP_LOCK_COUNT 32U -static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT]; - -#define SWAP_LOCK(addr) \ - &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT] - - -int32_t android_atomic_acquire_load(volatile const int32_t* addr) -{ - return *addr; -} - -int32_t android_atomic_release_load(volatile const int32_t* addr) -{ - return *addr; -} - -void android_atomic_acquire_store(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_release_cas(oldValue, value, addr)); -} - -void android_atomic_release_store(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_release_cas(oldValue, value, addr)); -} - -int32_t android_atomic_inc(volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_release_cas(oldValue, oldValue+1, addr)); - return oldValue; -} - -int32_t android_atomic_dec(volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_release_cas(oldValue, oldValue-1, addr)); - return oldValue; -} - -int32_t android_atomic_add(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_release_cas(oldValue, oldValue+value, addr)); - return oldValue; -} - -int32_t android_atomic_and(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_release_cas(oldValue, oldValue&value, addr)); - return oldValue; -} - -int32_t android_atomic_or(int32_t value, volatile int32_t* addr) { - int32_t oldValue; - do { - oldValue = *addr; - } while (android_atomic_release_cas(oldValue, oldValue|value, addr)); - return oldValue; -} - -int android_atomic_acquire_cmpxchg(int32_t oldvalue, int32_t newvalue, - volatile int32_t* addr) { - return android_atomic_release_cmpxchg(oldValue, newValue, addr); -} - -int android_atomic_release_cmpxchg(int32_t oldvalue, int32_t newvalue, - volatile int32_t* addr) { - int result; - pthread_mutex_t* lock = SWAP_LOCK(addr); - - pthread_mutex_lock(lock); - - if (*addr == oldvalue) { - *addr = newvalue; - result = 0; - } else { - result = 1; - } - pthread_mutex_unlock(lock); - return result; -} - diff --git a/libcutils/atomic.c b/libcutils/atomic.c index f6cd8b0..1484ef8 100644 --- a/libcutils/atomic.c +++ b/libcutils/atomic.c @@ -14,6 +14,6 @@ * limitations under the License. */ -#define inline +#define ANDROID_ATOMIC_INLINE #include <cutils/atomic-inline.h> 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/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c index 1ced147..c327a55 100644 --- a/libcutils/dlmalloc_stubs.c +++ b/libcutils/dlmalloc_stubs.c @@ -14,16 +14,22 @@ * limitations under the License. */ -/* No-op stubs for functions defined in system/bionic/bionic/dlmalloc.c. - */ -void dlmalloc_walk_free_pages() -{ -} +#include "../../../bionic/libc/bionic/dlmalloc.h" +#include "cutils/log.h" -void dlmalloc_walk_heap() +/* + * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These + * are used in host builds, as the host libc will not contain these + * functions. + */ +void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg) { + ALOGW("Called host unimplemented stub: dlmalloc_inspect_all"); } -void dlmalloc_trim() +int dlmalloc_trim(size_t unused) { + ALOGW("Called host unimplemented stub: dlmalloc_trim"); + return 0; } diff --git a/libcutils/fs.c b/libcutils/fs.c new file mode 100644 index 0000000..1226d44 --- /dev/null +++ b/libcutils/fs.c @@ -0,0 +1,142 @@ +/* + * 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 LOG_TAG "cutils" + +#include <cutils/fs.h> +#include <cutils/log.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <limits.h> + +#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) +#define BUF_SIZE 64 + +int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) { + // Check if path needs to be created + struct stat sb; + if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) { + if (errno == ENOENT) { + goto create; + } else { + ALOGE("Failed to lstat(%s): %s", path, strerror(errno)); + return -1; + } + } + + // Exists, verify status + if (!S_ISDIR(sb.st_mode)) { + ALOGE("Not a directory: %s", path); + return -1; + } + if (((sb.st_mode & ALL_PERMS) == mode) && (sb.st_uid == uid) && (sb.st_gid == gid)) { + return 0; + } else { + goto fixup; + } + +create: + if (TEMP_FAILURE_RETRY(mkdir(path, mode)) == -1) { + if (errno != EEXIST) { + ALOGE("Failed to mkdir(%s): %s", path, strerror(errno)); + return -1; + } + } + +fixup: + if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) { + ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno)); + return -1; + } + if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) { + ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno)); + return -1; + } + + return 0; +} + +int fs_read_atomic_int(const char* path, int* out_value) { + int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY)); + if (fd == -1) { + ALOGE("Failed to read %s: %s", path, strerror(errno)); + return -1; + } + + char buf[BUF_SIZE]; + if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) { + ALOGE("Failed to read %s: %s", path, strerror(errno)); + goto fail; + } + if (sscanf(buf, "%d", out_value) != 1) { + ALOGE("Failed to parse %s: %s", path, strerror(errno)); + goto fail; + } + close(fd); + return 0; + +fail: + close(fd); + *out_value = -1; + return -1; +} + +int fs_write_atomic_int(const char* path, int value) { + char temp[PATH_MAX]; + if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) { + ALOGE("Path too long"); + return -1; + } + + int fd = TEMP_FAILURE_RETRY(mkstemp(temp)); + if (fd == -1) { + ALOGE("Failed to open %s: %s", temp, strerror(errno)); + return -1; + } + + char buf[BUF_SIZE]; + int len = snprintf(buf, BUF_SIZE, "%d", value) + 1; + if (len > BUF_SIZE) { + ALOGE("Value %d too large: %s", value, strerror(errno)); + goto fail; + } + if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) { + ALOGE("Failed to write %s: %s", temp, strerror(errno)); + goto fail; + } + if (close(fd) == -1) { + ALOGE("Failed to close %s: %s", temp, strerror(errno)); + goto fail_closed; + } + + if (rename(temp, path) == -1) { + ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno)); + goto fail_closed; + } + + return 0; + +fail: + close(fd); +fail_closed: + unlink(temp); + return -1; +} diff --git a/libcutils/mspace.c b/libcutils/mspace.c deleted file mode 100644 index 6d3b35c..0000000 --- a/libcutils/mspace.c +++ /dev/null @@ -1,286 +0,0 @@ -/* Copyright 2006 The Android Open Source Project */ - -/* A wrapper file for dlmalloc.c that compiles in the - * mspace_*() functions, which provide an interface for - * creating multiple heaps. - */ -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdint.h> -#include <sys/ioctl.h> - -#include <cutils/ashmem.h> - -/* It's a pain getting the mallinfo stuff to work - * with Linux, OSX, and klibc, so just turn it off - * for now. - * TODO: make mallinfo work - */ -#define NO_MALLINFO 1 - -/* Allow setting the maximum heap footprint. - */ -#define USE_MAX_ALLOWED_FOOTPRINT 1 - -/* Don't try to trim memory. - * TODO: support this. - */ -#define MORECORE_CANNOT_TRIM 1 - -/* Use mmap()d anonymous memory to guarantee - * that an mspace is contiguous. - * - * create_mspace() won't work right if this is - * defined, so hide the definition of it and - * break any users at build time. - */ -#define USE_CONTIGUOUS_MSPACES 1 -#if USE_CONTIGUOUS_MSPACES -/* This combination of settings forces sys_alloc() - * to always use MORECORE(). It won't expect the - * results to be contiguous, but we'll guarantee - * that they are. - */ -#define HAVE_MMAP 0 -#define HAVE_MORECORE 1 -#define MORECORE_CONTIGUOUS 0 -/* m is always the appropriate local when MORECORE() is called. */ -#define MORECORE(S) contiguous_mspace_morecore(m, S) -#define create_mspace HIDDEN_create_mspace_HIDDEN -#define destroy_mspace HIDDEN_destroy_mspace_HIDDEN -typedef struct malloc_state *mstate0; -static void *contiguous_mspace_morecore(mstate0 m, ssize_t nb); -#endif - -#define MSPACES 1 -#define ONLY_MSPACES 1 -#include "../../../bionic/libc/bionic/dlmalloc.c" - -#ifndef PAGESIZE -#define PAGESIZE mparams.page_size -#endif - -#define ALIGN_UP(p, alignment) \ - (((uintptr_t)(p) + (alignment)-1) & ~((alignment)-1)) - -/* A direct copy of dlmalloc_usable_size(), - * which isn't compiled in when ONLY_MSPACES is set. - * The mspace parameter isn't actually necessary, - * but we include it to be consistent with the - * rest of the mspace_*() functions. - */ -size_t mspace_usable_size(mspace _unused, const void* mem) { - if (mem != 0) { - const mchunkptr p = mem2chunk(mem); - if (cinuse(p)) - return chunksize(p) - overhead_for(p); - } - return 0; -} - -#if USE_CONTIGUOUS_MSPACES -#include <sys/mman.h> -#include <limits.h> - -#define CONTIG_STATE_MAGIC 0xf00dd00d -struct mspace_contig_state { - unsigned int magic; - char *brk; - char *top; - mspace m; -}; - -static void *contiguous_mspace_morecore(mstate m, ssize_t nb) { - struct mspace_contig_state *cs; - char *oldbrk; - const unsigned int pagesize = PAGESIZE; - - cs = (struct mspace_contig_state *)((uintptr_t)m & ~(pagesize-1)); - assert(cs->magic == CONTIG_STATE_MAGIC); - assert(cs->m == m); -assert(nb >= 0); //xxx deal with the trim case - - oldbrk = cs->brk; - if (nb > 0) { - /* Break to the first page boundary that satisfies the request. - */ - char *newbrk = (char *)ALIGN_UP(oldbrk + nb, pagesize); - if (newbrk > cs->top) - return CMFAIL; - - /* Update the protection on the underlying memory. - * Pages we've given to dlmalloc are read/write, and - * pages we haven't are not accessable (read or write - * will cause a seg fault). - */ - if (mprotect(cs, newbrk - (char *)cs, PROT_READ | PROT_WRITE) < 0) - return CMFAIL; - if (newbrk != cs->top) { - if (mprotect(newbrk, cs->top - newbrk, PROT_NONE) < 0) - return CMFAIL; - } - - cs->brk = newbrk; - - /* Make sure that dlmalloc will merge this block with the - * initial block that was passed to create_mspace_with_base(). - * We don't care about extern vs. non-extern, so just clear it. - */ - m->seg.sflags &= ~EXTERN_BIT; - } - - return oldbrk; -} - -mspace create_contiguous_mspace_with_base(size_t starting_capacity, - size_t max_capacity, int locked, void *base) { - struct mspace_contig_state *cs; - unsigned int pagesize; - mstate m; - - init_mparams(); - pagesize = PAGESIZE; - assert(starting_capacity <= max_capacity); - assert(((uintptr_t)base & (pagesize-1)) == 0); - assert(((uintptr_t)max_capacity & (pagesize-1)) == 0); - starting_capacity = (size_t)ALIGN_UP(starting_capacity, pagesize); - - /* Make the first page read/write. dlmalloc needs to use that page. - */ - if (mprotect(base, starting_capacity, PROT_READ | PROT_WRITE) < 0) { - goto error; - } - - /* Create the mspace, pointing to the memory given. - */ - m = create_mspace_with_base((char *)base + sizeof(*cs), starting_capacity, - locked); - if (m == (mspace)0) { - goto error; - } - /* Make sure that m is in the same page as base. - */ - assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)base); - /* Use some space for the information that our MORECORE needs. - */ - cs = (struct mspace_contig_state *)base; - - /* Find out exactly how much of the memory the mspace - * is using. - */ - cs->brk = m->seg.base + m->seg.size; - cs->top = (char *)base + max_capacity; - - assert((char *)base <= cs->brk); - assert(cs->brk <= cs->top); - /* Prevent access to the memory we haven't handed out yet. - */ - if (cs->brk != cs->top) { - /* mprotect() requires page-aligned arguments, but it's possible - * for cs->brk not to be page-aligned at this point. - */ - char *prot_brk = (char *)ALIGN_UP(cs->brk, pagesize); - if ((mprotect(base, prot_brk - (char *)base, PROT_READ | PROT_WRITE) < 0) || - (mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)) { - goto error; - } - } - - cs->m = m; - cs->magic = CONTIG_STATE_MAGIC; - - return (mspace)m; - -error: - return (mspace)0; -} - - -mspace create_contiguous_mspace_with_name(size_t starting_capacity, - size_t max_capacity, int locked, char const *name) { - int fd, ret; - char buf[ASHMEM_NAME_LEN] = "mspace"; - void *base; - unsigned int pagesize; - mstate m; - - if (starting_capacity > max_capacity) - return (mspace)0; - - init_mparams(); - pagesize = PAGESIZE; - - /* Create the anonymous memory that will back the mspace. - * This reserves all of the virtual address space we could - * ever need. Physical pages will be mapped as the memory - * is touched. - * - * Align max_capacity to a whole page. - */ - max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize); - - if (name) - snprintf(buf, sizeof(buf), "mspace/%s", name); - fd = ashmem_create_region(buf, max_capacity); - if (fd < 0) - return (mspace)0; - - base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); - if (base == MAP_FAILED) - return (mspace)0; - - /* Make sure that base is at the beginning of a page. - */ - assert(((uintptr_t)base & (pagesize-1)) == 0); - - m = create_contiguous_mspace_with_base(starting_capacity, max_capacity, - locked, base); - if (m == 0) { - munmap(base, max_capacity); - } - return m; -} - -mspace create_contiguous_mspace(size_t starting_capacity, - size_t max_capacity, int locked) { - return create_contiguous_mspace_with_name(starting_capacity, - max_capacity, locked, NULL); -} - -size_t destroy_contiguous_mspace(mspace msp) { - mstate ms = (mstate)msp; - - if (ok_magic(ms)) { - struct mspace_contig_state *cs; - size_t length; - const unsigned int pagesize = PAGESIZE; - - cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1)); - assert(cs->magic == CONTIG_STATE_MAGIC); - assert(cs->m == ms); - - length = cs->top - (char *)cs; - if (munmap((char *)cs, length) != 0) - return length; - } - else { - USAGE_ERROR_ACTION(ms, ms); - } - return 0; -} - -void *contiguous_mspace_sbrk0(mspace msp) { - struct mspace_contig_state *cs; - mstate ms; - const unsigned int pagesize = PAGESIZE; - - ms = (mstate)msp; - cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1)); - assert(cs->magic == CONTIG_STATE_MAGIC); - assert(cs->m == ms); - return cs->brk; -} -#endif diff --git a/nexus/LoopController.cpp b/libcutils/multiuser.c index f8e01d7..7c74bb8 100644 --- a/nexus/LoopController.cpp +++ b/libcutils/multiuser.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,16 @@ * limitations under the License. */ -#include <errno.h> +#include <cutils/multiuser.h> -#include "LoopController.h" -#include "PropertyManager.h" +userid_t multiuser_get_user_id(uid_t uid) { + return uid / MULTIUSER_APP_PER_USER_RANGE; +} + +appid_t multiuser_get_app_id(uid_t uid) { + return uid % MULTIUSER_APP_PER_USER_RANGE; +} -LoopController::LoopController(PropertyManager *propmngr, - IControllerHandler *handlers) : - Controller("loop", propmngr, handlers) { +uid_t multiuser_get_uid(userid_t userId, appid_t appId) { + return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); } diff --git a/libcutils/private.h b/libcutils/private.h deleted file mode 100644 index 2837b70..0000000 --- a/libcutils/private.h +++ /dev/null @@ -1,368 +0,0 @@ -#ifndef PRIVATE_H - -#define PRIVATE_H - -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -/* -** This header is for use ONLY with the time conversion code. -** There is no guarantee that it will remain unchanged, -** or that it will remain at all. -** Do NOT copy it to any system include directory. -** Thank you! -*/ - -/* -** ID -*/ - -#ifndef lint -#ifndef NOID -static char privatehid[] = "@(#)private.h 8.2"; -#endif /* !defined NOID */ -#endif /* !defined lint */ - -#define GRANDPARENTED "Local time zone must be set--see zic manual page" - -/* -** Defaults for preprocessor symbols. -** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. -*/ - -#ifndef HAVE_ADJTIME -#define HAVE_ADJTIME 1 -#endif /* !defined HAVE_ADJTIME */ - -#ifndef HAVE_GETTEXT -#define HAVE_GETTEXT 0 -#endif /* !defined HAVE_GETTEXT */ - -#ifndef HAVE_INCOMPATIBLE_CTIME_R -#define HAVE_INCOMPATIBLE_CTIME_R 0 -#endif /* !defined INCOMPATIBLE_CTIME_R */ - -#ifndef HAVE_SETTIMEOFDAY -#define HAVE_SETTIMEOFDAY 3 -#endif /* !defined HAVE_SETTIMEOFDAY */ - -#ifndef HAVE_STRERROR -#define HAVE_STRERROR 1 -#endif /* !defined HAVE_STRERROR */ - -#ifndef HAVE_SYMLINK -#define HAVE_SYMLINK 1 -#endif /* !defined HAVE_SYMLINK */ - -#ifndef HAVE_SYS_STAT_H -#define HAVE_SYS_STAT_H 1 -#endif /* !defined HAVE_SYS_STAT_H */ - -#ifndef HAVE_SYS_WAIT_H -#define HAVE_SYS_WAIT_H 1 -#endif /* !defined HAVE_SYS_WAIT_H */ - -#ifndef HAVE_UNISTD_H -#define HAVE_UNISTD_H 1 -#endif /* !defined HAVE_UNISTD_H */ - -#ifndef HAVE_UTMPX_H -#define HAVE_UTMPX_H 0 -#endif /* !defined HAVE_UTMPX_H */ - -#ifndef LOCALE_HOME -#define LOCALE_HOME "/usr/lib/locale" -#endif /* !defined LOCALE_HOME */ - -#if HAVE_INCOMPATIBLE_CTIME_R -#define asctime_r _incompatible_asctime_r -#define ctime_r _incompatible_ctime_r -#endif /* HAVE_INCOMPATIBLE_CTIME_R */ - -/* -** Nested includes -*/ - -#include "sys/types.h" /* for time_t */ -#include "stdio.h" -#include "errno.h" -#include "string.h" -#include "limits.h" /* for CHAR_BIT et al. */ -#include "time.h" -#include "stdlib.h" - -#if HAVE_GETTEXT -#include "libintl.h" -#endif /* HAVE_GETTEXT */ - -#if HAVE_SYS_WAIT_H -#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */ -#endif /* HAVE_SYS_WAIT_H */ - -#ifndef WIFEXITED -#define WIFEXITED(status) (((status) & 0xff) == 0) -#endif /* !defined WIFEXITED */ -#ifndef WEXITSTATUS -#define WEXITSTATUS(status) (((status) >> 8) & 0xff) -#endif /* !defined WEXITSTATUS */ - -#if HAVE_UNISTD_H -#include "unistd.h" /* for F_OK and R_OK */ -#endif /* HAVE_UNISTD_H */ - -#if !HAVE_UNISTD_H -#ifndef F_OK -#define F_OK 0 -#endif /* !defined F_OK */ -#ifndef R_OK -#define R_OK 4 -#endif /* !defined R_OK */ -#endif /* !HAVE_UNISTD_H */ - -/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ -#define is_digit(c) ((unsigned)(c) - '0' <= 9) - -/* -** Define HAVE_STDINT_H's default value here, rather than at the -** start, since __GLIBC__'s value depends on previously-included -** files. -** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.) -*/ -#ifndef HAVE_STDINT_H -#define HAVE_STDINT_H \ - (199901 <= __STDC_VERSION__ || \ - 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__))) -#endif /* !defined HAVE_STDINT_H */ - -#if HAVE_STDINT_H -#include "stdint.h" -#endif /* !HAVE_STDINT_H */ - -#ifndef INT_FAST64_MAX -/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ -#if defined LLONG_MAX || defined __LONG_LONG_MAX__ -typedef long long int_fast64_t; -#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ -#if (LONG_MAX >> 31) < 0xffffffff -Please use a compiler that supports a 64-bit integer type (or wider); -you may need to compile with "-DHAVE_STDINT_H". -#endif /* (LONG_MAX >> 31) < 0xffffffff */ -typedef long int_fast64_t; -#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ -#endif /* !defined INT_FAST64_MAX */ - -#ifndef INT32_MAX -#define INT32_MAX 0x7fffffff -#endif /* !defined INT32_MAX */ -#ifndef INT32_MIN -#define INT32_MIN (-1 - INT32_MAX) -#endif /* !defined INT32_MIN */ - -/* -** Workarounds for compilers/systems. -*/ - -/* -** If your compiler lacks prototypes, "#define P(x) ()". -*/ - -#ifndef P -#define P(x) x -#endif /* !defined P */ - -/* -** SunOS 4.1.1 headers lack EXIT_SUCCESS. -*/ - -#ifndef EXIT_SUCCESS -#define EXIT_SUCCESS 0 -#endif /* !defined EXIT_SUCCESS */ - -/* -** SunOS 4.1.1 headers lack EXIT_FAILURE. -*/ - -#ifndef EXIT_FAILURE -#define EXIT_FAILURE 1 -#endif /* !defined EXIT_FAILURE */ - -/* -** SunOS 4.1.1 headers lack FILENAME_MAX. -*/ - -#ifndef FILENAME_MAX - -#ifndef MAXPATHLEN -#ifdef unix -#include "sys/param.h" -#endif /* defined unix */ -#endif /* !defined MAXPATHLEN */ - -#ifdef MAXPATHLEN -#define FILENAME_MAX MAXPATHLEN -#endif /* defined MAXPATHLEN */ -#ifndef MAXPATHLEN -#define FILENAME_MAX 1024 /* Pure guesswork */ -#endif /* !defined MAXPATHLEN */ - -#endif /* !defined FILENAME_MAX */ - -/* -** SunOS 4.1.1 libraries lack remove. -*/ - -#ifndef remove -extern int unlink P((const char * filename)); -#define remove unlink -#endif /* !defined remove */ - -/* -** Some ancient errno.h implementations don't declare errno. -** But some newer errno.h implementations define it as a macro. -** Fix the former without affecting the latter. -*/ - -#ifndef errno -extern int errno; -#endif /* !defined errno */ - -/* -** Some time.h implementations don't declare asctime_r. -** Others might define it as a macro. -** Fix the former without affecting the latter. -*/ - -#ifndef asctime_r -extern char * asctime_r(); -#endif - -/* -** Private function declarations. -*/ - -char * icalloc P((int nelem, int elsize)); -char * icatalloc P((char * old, const char * new)); -char * icpyalloc P((const char * string)); -char * imalloc P((int n)); -void * irealloc P((void * pointer, int size)); -void icfree P((char * pointer)); -void ifree P((char * pointer)); -const char * scheck P((const char * string, const char * format)); - -/* -** Finally, some convenience items. -*/ - -#ifndef TRUE -#define TRUE 1 -#endif /* !defined TRUE */ - -#ifndef FALSE -#define FALSE 0 -#endif /* !defined FALSE */ - -#ifndef TYPE_BIT -#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) -#endif /* !defined TYPE_BIT */ - -#ifndef TYPE_SIGNED -#define TYPE_SIGNED(type) (((type) -1) < 0) -#endif /* !defined TYPE_SIGNED */ - -/* -** Since the definition of TYPE_INTEGRAL contains floating point numbers, -** it cannot be used in preprocessor directives. -*/ - -#ifndef TYPE_INTEGRAL -#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) -#endif /* !defined TYPE_INTEGRAL */ - -#ifndef INT_STRLEN_MAXIMUM -/* -** 302 / 1000 is log10(2.0) rounded up. -** Subtract one for the sign bit if the type is signed; -** add one for integer division truncation; -** add one more for a minus sign if the type is signed. -*/ -#define INT_STRLEN_MAXIMUM(type) \ - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ - 1 + TYPE_SIGNED(type)) -#endif /* !defined INT_STRLEN_MAXIMUM */ - -/* -** INITIALIZE(x) -*/ - -#ifndef GNUC_or_lint -#ifdef lint -#define GNUC_or_lint -#endif /* defined lint */ -#ifndef lint -#ifdef __GNUC__ -#define GNUC_or_lint -#endif /* defined __GNUC__ */ -#endif /* !defined lint */ -#endif /* !defined GNUC_or_lint */ - -#ifndef INITIALIZE -#ifdef GNUC_or_lint -#define INITIALIZE(x) ((x) = 0) -#endif /* defined GNUC_or_lint */ -#ifndef GNUC_or_lint -#define INITIALIZE(x) -#endif /* !defined GNUC_or_lint */ -#endif /* !defined INITIALIZE */ - -/* -** For the benefit of GNU folk... -** `_(MSGID)' uses the current locale's message library string for MSGID. -** The default is to use gettext if available, and use MSGID otherwise. -*/ - -#ifndef _ -#if HAVE_GETTEXT -#define _(msgid) gettext(msgid) -#else /* !HAVE_GETTEXT */ -#define _(msgid) msgid -#endif /* !HAVE_GETTEXT */ -#endif /* !defined _ */ - -#ifndef TZ_DOMAIN -#define TZ_DOMAIN "tz" -#endif /* !defined TZ_DOMAIN */ - -#if HAVE_INCOMPATIBLE_CTIME_R -#undef asctime_r -#undef ctime_r -char *asctime_r P((struct tm const *, char *)); -char *ctime_r P((time_t const *, char *)); -#endif /* HAVE_INCOMPATIBLE_CTIME_R */ - -#ifndef YEARSPERREPEAT -#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ -#endif /* !defined YEARSPERREPEAT */ - -/* -** The Gregorian year averages 365.2425 days, which is 31556952 seconds. -*/ - -#ifndef AVGSECSPERYEAR -#define AVGSECSPERYEAR 31556952L -#endif /* !defined AVGSECSPERYEAR */ - -#ifndef SECSPERREPEAT -#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR) -#endif /* !defined SECSPERREPEAT */ - -#ifndef SECSPERREPEAT_BITS -#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ -#endif /* !defined SECSPERREPEAT_BITS */ - -/* -** UNIX was a registered trademark of The Open Group in 2003. -*/ - -#endif /* !defined PRIVATE_H */ 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/properties.c b/libcutils/properties.c index f732ec0..28d8b2f 100644 --- a/libcutils/properties.c +++ b/libcutils/properties.c @@ -52,19 +52,28 @@ int property_get(const char *key, char *value, const char *default_value) return len; } -int property_list(void (*propfn)(const char *key, const char *value, void *cookie), - void *cookie) +struct property_list_callback_data +{ + void (*propfn)(const char *key, const char *value, void *cookie); + void *cookie; +}; + +static void property_list_callback(const prop_info *pi, 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; + struct property_list_callback_data *data = cookie; + + __system_property_read(pi, name, value); + data->propfn(name, value, data->cookie); +} + +int property_list( + void (*propfn)(const char *key, const char *value, void *cookie), + void *cookie) +{ + struct property_list_callback_data data = { propfn, cookie }; + return __system_property_foreach(property_list_callback, &data); } #elif defined(HAVE_SYSTEM_PROPERTY_SERVER) 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/tests/Android.mk b/libcutils/tests/Android.mk new file mode 100644 index 0000000..6571161 --- /dev/null +++ b/libcutils/tests/Android.mk @@ -0,0 +1 @@ +include $(all-subdir-makefiles) diff --git a/libcutils/tests/memset_mips/Android.mk b/libcutils/tests/memset_mips/Android.mk new file mode 100644 index 0000000..c22fca9 --- /dev/null +++ b/libcutils/tests/memset_mips/Android.mk @@ -0,0 +1,23 @@ +# Copyright 2012 The Android Open Source Project + +ifeq ($(TARGET_ARCH),mips) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + test_memset.c \ + android_memset_dumb.S \ + android_memset_test.S \ + memset_cmips.S \ + memset_omips.S + +LOCAL_MODULE:= test_memset + +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) + +endif diff --git a/libcutils/tests/memset_mips/android_memset_dumb.S b/libcutils/tests/memset_mips/android_memset_dumb.S new file mode 100644 index 0000000..c8a1a37 --- /dev/null +++ b/libcutils/tests/memset_mips/android_memset_dumb.S @@ -0,0 +1,36 @@ + .global android_memset16_dumb + .type android_memset16_dumb, @function +android_memset16_dumb: + .ent android_memset16_dumb + + .set noreorder + beqz $a2,9f + srl $a2,1 + +1: sh $a1,($a0) + subu $a2,1 + bnez $a2,1b + addu $a0,2 + .set reorder + +9: j $ra + .end android_memset16_dumb + .size android_memset16_dumb,.-android_memset16_dumb + + .global android_memset32_dumb + .type android_memset32_dumb, @function +android_memset32_dumb: + .ent android_memset32_dumb + .set noreorder + beqz $a2,9f + srl $a2,2 + +1: sw $a1,($a0) + subu $a2,1 + bnez $a2,1b + addu $a0,4 + .set reorder + +9: j $ra + .end android_memset32_dumb + .size android_memset32_dumb,.-android_memset32_dumb diff --git a/libcutils/tests/memset_mips/android_memset_test.S b/libcutils/tests/memset_mips/android_memset_test.S new file mode 100644 index 0000000..e918843 --- /dev/null +++ b/libcutils/tests/memset_mips/android_memset_test.S @@ -0,0 +1,152 @@ +/* + * Copyright (C) 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. + */ + +#ifdef NDEBUG +#define DBG # +#else +#define DBG +#endif + + .text + .align + + /* + * Optimized memset16 for MIPS + * + * void android_memset16_test(uint16_t* dst, uint16_t value, size_t size); + * + */ + + .global android_memset16_test + .type android_memset16_test, @function +android_memset16_test: + .ent android_memset16_test + .set noreorder + + /* Check parameters */ +DBG andi $t0,$a0,1 /* $a0 must be halfword aligned */ +DBG tne $t0 +DBG lui $t1,0xffff /* $a1 must be 16bits */ +DBG and $t1,$a1 +DBG tne $t1 +DBG andi $t2,$a2,1 /* $a2 must be even */ +DBG tne $t2 + +#if (__mips==32) && (__mips_isa_rev>=2) + ins $a2,$0,0,1 +#else + li $t0,~1 + and $a2,$t0 +#endif + + move $t8,$ra + blez $a2,9f /* Anything to do? */ + andi $t0,$a0,2 /* Check dst alignment */ + /* Expand value to 32 bits and check destination alignment */ +#if (__mips==32) && (__mips_isa_rev>=2) + beqz $t0,.Laligned32 /* dst is 32 bit aligned */ + ins $a1,$a1,16,16 +#else + sll $t2,$a1,16 + beqz $t0,.Laligned32 /* dst is 32 bit aligned */ + or $a1,$t2 +#endif + sh $a1,($a0) /* do one halfword to get aligned */ + subu $a2,2 + addu $a0,2 + +.Laligned32: + and $t1,$a2,63 /* is there enough left to do a full 64 byte loop? */ + beq $a2,$t1,1f + subu $t2,$a2,$t1 /* $t2 is the number of bytes to do in loop64 */ + addu $t3,$a0,$t2 /* $t3 is the end marker for loop64 */ + subu $a2,$t2 +.Lloop64: + addu $a0,64 + sw $a1,-64($a0) + sw $a1,-60($a0) + sw $a1,-56($a0) + sw $a1,-52($a0) + sw $a1,-48($a0) + sw $a1,-44($a0) + sw $a1,-40($a0) + sw $a1,-36($a0) + sw $a1,-32($a0) + sw $a1,-28($a0) + sw $a1,-24($a0) + sw $a1,-20($a0) + sw $a1,-16($a0) + sw $a1,-12($a0) + sw $a1,-8($a0) + bne $a0,$t3,.Lloop64 + sw $a1,-4($a0) + + /* Do the last 0..62 bytes */ +1: li $t0,64+12 + andi $t1,$a2,0x3c /* $t1 how many bytes to store using sw */ + bal 1f + subu $t0,$t1 /* 64+12-$t0 is offset to jump from 1f */ +1: addu $ra,$t0 + j $ra + subu $a2,$t1 +2: sw $a1,60($a0) + sw $a1,56($a0) + sw $a1,52($a0) + sw $a1,48($a0) + sw $a1,44($a0) + sw $a1,40($a0) + sw $a1,36($a0) + sw $a1,32($a0) + sw $a1,28($a0) + sw $a1,24($a0) + sw $a1,20($a0) + sw $a1,16($a0) + sw $a1,12($a0) + sw $a1,8($a0) + sw $a1,4($a0) + sw $a1,0($a0) + + beqz $a2,9f + addu $a0,$t1 + sh $a1,($a0) + +9: j $t8 + nop + .end android_memset16_test + .size android_memset16_test,.-android_memset16_test + + /* + * Optimized memset32 for MIPS + * + * void android_memset32_test(uint32_t* dst, uint32_t value, size_t size); + * + */ + .global android_memset32_test + .type android_memset32_test, @function +android_memset32_test: + .ent android_memset32_test + .set noreorder + + /* Check parameters */ +DBG andi $t0,$a0,3 /* $a0 must be word aligned */ +DBG tne $t0 +DBG andi $t2,$a2,3 /* $a2 must be a multiple of 4 bytes */ +DBG tne $t2 + + b .Laligned32 + move $t8,$ra + .end android_memset32_test + .size android_memset32_test,.-android_memset32_test diff --git a/libcutils/tests/memset_mips/memset_cmips.S b/libcutils/tests/memset_mips/memset_cmips.S new file mode 100644 index 0000000..f8f3a91 --- /dev/null +++ b/libcutils/tests/memset_mips/memset_cmips.S @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2009 + * MIPS Technologies, Inc., California. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/************************************************************************ + * + * memset.S, version "64h" with 1 cache line horizon for "pref 30" and 14 nops + * Version: "043009" + * + ************************************************************************/ + + +/************************************************************************ + * Include files + ************************************************************************/ + +#include "machine/asm.h" + +/* + * This routine could be optimized for MIPS64. The current code only + * uses MIPS32 instructions. + */ + +#if defined(__MIPSEB__) +# define SWHI swl /* high part is left in big-endian */ +#endif + +#if defined(__MIPSEL__) +# define SWHI swr /* high part is right in little-endian */ +#endif + +#if !(defined(XGPROF) || defined(XPROF)) +#undef SETUP_GP +#define SETUP_GP +#endif + +LEAF(memset_cmips,0) + + .set noreorder + .set noat + + addu t0,a0,a2 # t0 is the "past the end" address + slti AT,a2,4 # is a2 less than 4? + bne AT,zero,.Llast4 # if yes, go to last4 + move v0,a0 # memset returns the dst pointer + + beq a1,zero,.Lset0 + subu v1,zero,a0 + + # smear byte into 32 bit word +#if (__mips==32) && (__mips_isa_rev>=2) + ins a1, a1, 8, 8 # Replicate fill byte into half-word. + ins a1, a1, 16, 16 # Replicate fill byte into word. +#else + and a1,0xff + sll AT,a1,8 + or a1,AT + sll AT,a1,16 + or a1,AT +#endif + +.Lset0: andi v1,v1,0x3 # word-unaligned address? + beq v1,zero,.Laligned # v1 is the unalignment count + subu a2,a2,v1 + SWHI a1,0(a0) + addu a0,a0,v1 + +# Here we have the "word-aligned" a0 (until the "last4") +.Laligned: + andi t8,a2,0x3f # any 64-byte chunks? + # t8 is the byte count past 64-byte chunks + beq a2,t8,.Lchk8w # when a2==t8, no 64-byte chunks + # There will be at most 1 32-byte chunk then + subu a3,a2,t8 # subtract from a2 the reminder + # Here a3 counts bytes in 16w chunks + addu a3,a0,a3 # Now a3 is the final dst after 64-byte chunks + +# Find out, if there are any 64-byte chunks after which will be still at least +# 96 bytes left. The value "96" is calculated as needed buffer for +# "pref 30,64(a0)" prefetch, which can be used as "pref 30,0(a0)" after +# incrementing "a0" by 64. +# For "a2" below 160 there will be no such "pref 30 safe" 64-byte chunk. +# + sltiu v1,a2,160 + bgtz v1,.Lloop16w_nopref30 # skip "pref 30,0(a0)" + subu t7,a2,96 # subtract "pref 30 unsafe" region + # below we have at least 1 64-byte chunk which is "pref 30 safe" + andi t6,t7,0x3f # t6 is past "64-byte safe chunks" reminder + subu t5,t7,t6 # subtract from t7 the reminder + # Here t5 counts bytes in 16w "safe" chunks + addu t4,a0,t5 # Now t4 is the dst after 64-byte "safe" chunks + +# Don't use "pref 30,0(a0)" for a0 in a "middle" of a cache line +# pref 30,0(a0) +# Here we are in the region, where it is safe to use "pref 30,64(a0)" +.Lloop16w: + addiu a0,a0,64 + pref 30,-32(a0) # continue setting up the dest, addr 64-32 + sw a1,-64(a0) + sw a1,-60(a0) + sw a1,-56(a0) + sw a1,-52(a0) + sw a1,-48(a0) + sw a1,-44(a0) + sw a1,-40(a0) + sw a1,-36(a0) + nop + nop # the extra nop instructions help to balance + nop # cycles needed for "store" + "fill" + "evict" + nop # For 64byte store there are needed 8 fill + nop # and 8 evict cycles, i.e. at least 32 instr. + nop + nop + pref 30,0(a0) # continue setting up the dest, addr 64-0 + sw a1,-32(a0) + sw a1,-28(a0) + sw a1,-24(a0) + sw a1,-20(a0) + sw a1,-16(a0) + sw a1,-12(a0) + sw a1,-8(a0) + sw a1,-4(a0) + nop + nop + nop + nop # NOTE: adding 14 nop-s instead of 12 nop-s + nop # gives better results for "fast" memory + nop + bne a0,t4,.Lloop16w + nop + + beq a0,a3,.Lchk8w # maybe no more 64-byte chunks? + nop # this "delayed slot" is useless ... + +.Lloop16w_nopref30: # there could be up to 3 "64-byte nopref30" chunks + addiu a0,a0,64 + sw a1,-64(a0) + sw a1,-60(a0) + sw a1,-56(a0) + sw a1,-52(a0) + sw a1,-48(a0) + sw a1,-44(a0) + sw a1,-40(a0) + sw a1,-36(a0) + sw a1,-32(a0) + sw a1,-28(a0) + sw a1,-24(a0) + sw a1,-20(a0) + sw a1,-16(a0) + sw a1,-12(a0) + sw a1,-8(a0) + bne a0,a3,.Lloop16w_nopref30 + sw a1,-4(a0) + +.Lchk8w: # t8 here is the byte count past 64-byte chunks + + andi t7,t8,0x1f # is there a 32-byte chunk? + # the t7 is the reminder count past 32-bytes + beq t8,t7,.Lchk1w # when t8==t7, no 32-byte chunk + move a2,t7 + + sw a1,0(a0) + sw a1,4(a0) + sw a1,8(a0) + sw a1,12(a0) + sw a1,16(a0) + sw a1,20(a0) + sw a1,24(a0) + sw a1,28(a0) + addiu a0,a0,32 + +.Lchk1w: + andi t8,a2,0x3 # now t8 is the reminder past 1w chunks + beq a2,t8,.Llast4 + subu a3,a2,t8 # a3 is the count of bytes in 1w chunks + addu a3,a0,a3 # now a3 is the dst address past the 1w chunks + +# copying in words (4-byte chunks) +.LwordCopy_loop: + addiu a0,a0,4 + bne a0,a3,.LwordCopy_loop + sw a1,-4(a0) + +.Llast4:beq a0,t0,.Llast4e +.Llast4l:addiu a0,a0,1 + bne a0,t0,.Llast4l + sb a1,-1(a0) + +.Llast4e: + j ra + nop + + .set at + .set reorder + +END(memset_cmips) + + +/************************************************************************ + * Implementation : Static functions + ************************************************************************/ + diff --git a/libcutils/tests/memset_mips/memset_omips.S b/libcutils/tests/memset_mips/memset_omips.S new file mode 100644 index 0000000..4c47001 --- /dev/null +++ b/libcutils/tests/memset_mips/memset_omips.S @@ -0,0 +1,90 @@ +/* Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Hartvig Ekner <hartvige@mips.com>, 2002. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* void *memset_omips(void *s, int c, size_t n). */ + +#include "machine/asm.h" + +#ifdef __mips64 +#error mips32 code being compiled for mips64! +#endif + +#if defined(__MIPSEB__) +#error big-endian is not supported in Broadcom MIPS Android platform +# define SWHI swl /* high part is left in big-endian */ +#else +# define SWHI swr /* high part is right in little-endian */ +#endif + +LEAF (memset_omips,0) + .set noreorder + + slti t1, a2, 8 # Less than 8? + bne t1, zero, .Llast8 + move v0, a0 # Setup exit value before too late + + beq a1, zero, .Lueven # If zero pattern, no need to extend + andi a1, 0xff # Avoid problems with bogus arguments + sll t0, a1, 8 + or a1, t0 + sll t0, a1, 16 + or a1, t0 # a1 is now pattern in full word + +.Lueven: + subu t0, zero, a0 # Unaligned address? + andi t0, 0x3 + beq t0, zero, .Lchkw + subu a2, t0 + SWHI a1, 0(a0) # Yes, handle first unaligned part + addu a0, t0 # Now both a0 and a2 are updated + +.Lchkw: + andi t0, a2, 0x7 # Enough left for one loop iteration? + beq t0, a2, .Lchkl + subu a3, a2, t0 + addu a3, a0 # a3 is last loop address +1 + move a2, t0 # a2 is now # of bytes left after loop +.Lloopw: + addiu a0, 8 # Handle 2 words pr. iteration + sw a1, -8(a0) + bne a0, a3, .Lloopw + sw a1, -4(a0) + +.Lchkl: + andi t0, a2, 0x4 # Check if there is at least a full + beq t0, zero, .Llast8 # word remaining after the loop + subu a2, t0 + sw a1, 0(a0) # Yes... + addiu a0, 4 + +.Llast8: + blez a2, .Lexit # Handle last 8 bytes (if cnt>0) + addu a3, a2, a0 # a3 is last address +1 +.Llst8l: + addiu a0, 1 + bne a0, a3, .Llst8l + sb a1, -1(a0) +.Lexit: + j ra # Bye, bye + nop + + .set reorder +END (memset_omips) + + diff --git a/libcutils/tests/memset_mips/test_memset.c b/libcutils/tests/memset_mips/test_memset.c new file mode 100644 index 0000000..9705c65 --- /dev/null +++ b/libcutils/tests/memset_mips/test_memset.c @@ -0,0 +1,235 @@ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <cutils/memory.h> +#include <time.h> + +/* + * All systems must implement or emulate the rdhwr instruction to read + * the userlocal register. Systems that emulate also return teh count register + * when accessing register $2 so this should work on most systems + */ +#define USE_RDHWR + +#ifdef USE_RDHWR +#define UNITS "cycles" +#define SCALE 2 /* Most CPU's */ +static inline uint32_t +get_count(void) +{ + uint32_t res; + asm volatile (".set push; .set mips32r2; rdhwr %[res],$2; .set pop" : [res] "=r" (res) : : "memory"); + return res; +} +#else +#define UNITS "ns" +#define SCALE 1 +static inline uint32_t +get_count(void) +{ + struct timespec now; + uint32_t res; + clock_gettime(CLOCK_REALTIME, &now); + res = (uint32_t)(now.tv_sec * 1000000000LL + now.tv_nsec); + // printf ("now=%d.%09d res=%d\n", (int)now.tv_sec, (int)now.tv_nsec, res); + return res; +} +#endif + +uint32_t overhead; +void +measure_overhead(void) +{ + int i; + uint32_t start, stop, delta; + for (i = 0; i < 32; i++) { + start = get_count(); + stop = get_count(); + delta = stop - start; + if (overhead == 0 || delta < overhead) + overhead = delta; + } + printf("overhead is %d"UNITS"\n", overhead); +} + +uint32_t +timeone(void (*fn)(), void *d, uint32_t val, uint32_t bytes) +{ + uint32_t start, stop, delta; + start = get_count(); + (*fn)(d, val, bytes); + stop = get_count(); + delta = stop - start - overhead; + // printf ("start=0x%08x stop=0x%08x delta=0x%08x\n", start, stop, delta); + return delta * SCALE; +} + +/* define VERIFY to check that memset only touches the bytes it's supposed to */ +/*#define VERIFY*/ + +/* + * Using a big arena means that memset will most likely miss in the cache + * NB Enabling verification effectively warms up the cache... + */ +#define ARENASIZE 0x1000000 +#ifdef VERIFY +char arena[ARENASIZE+8]; /* Allow space for guard words */ +#else +char arena[ARENASIZE]; +#endif + +void +testone(char *tag, void (*fn)(), int trials, int minbytes, int maxbytes, int size, int threshold) +{ + int offset; + void *d; + void *p; + uint32_t v, notv = 0; + uint32_t n; + int i, units; + int totalunits = 0, totalbytes = 0, samples = 0; + + /* Reset RNG to ensure each test uses same random values */ + srand(0); /* FIXME should be able to use some other seed than 0 */ + + for (i = 0; i < trials; i++) { + n = minbytes + (rand() % (maxbytes-minbytes)); /* How many bytes to do */ + offset = ((rand() % (ARENASIZE-n))); /* Where to start */ + +#ifdef VERIFY + offset += 4; /* Allow space for guard word at beginning */ +#endif + v = rand(); + + /* Adjust alignment and sizes based on transfer size */ + switch (size) { + case 1: + v &= 0xff; + notv = ~v & 0xff; + break; + case 2: + v &= 0xffff; + notv = ~v & 0xffff; + offset &= ~1; + n &= ~1; + break; + case 4: + notv = ~v; + offset &= ~3; + n &= ~3; + break; + } + + d = &arena[offset]; + +#ifdef VERIFY + /* Initialise the area and guard words */ + for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) { + if (size == 1) + *(uint8_t *)p = notv; + else if (size == 2) + *(uint16_t *)p = notv; + else if (size == 4) + *(uint32_t *)p = notv; + } +#endif + units = timeone(fn, d, v, n); +#ifdef VERIFY + /* Check the area and guard words */ + for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) { + uint32_t got = 0; + if (size == 1) + got = *(uint8_t *)p; + else if (size == 2) + got = *(uint16_t *)p; + else if (size == 4) + got = *(uint32_t *)p; + if (p < (void *)&arena[offset]) { + if (got != notv) + printf ("%s: verify failure: preguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, got, n); + } + else if (p < (void *)&arena[offset+n]) { + if (got != v) + printf ("%s: verify failure: arena:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n); + } + else { + if (got != notv) + printf ("%s: verify failure: postguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n); + } + } +#endif + + /* If the cycle count looks reasonable include it in the statistics */ + if (units < threshold) { + totalbytes += n; + totalunits += units; + samples++; + } + } + + printf("%s: samples=%d avglen=%d avg" UNITS "=%d bp"UNITS"=%g\n", + tag, samples, totalbytes/samples, totalunits/samples, (double)totalbytes/(double)totalunits); +} + +extern void android_memset32_dumb(uint32_t* dst, uint32_t value, size_t size); +extern void android_memset16_dumb(uint32_t* dst, uint16_t value, size_t size); +extern void android_memset32_test(uint32_t* dst, uint32_t value, size_t size); +extern void android_memset16_test(uint32_t* dst, uint16_t value, size_t size); +extern void memset_cmips(void* dst, int value, size_t size); +extern void memset_omips(void* dst, int value, size_t size); + +int +main(int argc, char **argv) +{ + int i; + struct { + char *type; + int trials; + int minbytes, maxbytes; + } *pp, params[] = { + {"small", 10000, 0, 64}, + {"medium", 10000, 64, 512}, + {"large", 10000, 512, 1280}, + {"varied", 10000, 0, 1280}, + }; +#define NPARAMS (sizeof(params)/sizeof(params[0])) + struct { + char *name; + void (*fn)(); + int size; + } *fp, functions[] = { + {"dmemset16", (void (*)())android_memset16_dumb, 2}, + {"tmemset16", (void (*)())android_memset16_test, 2}, + {"lmemset16", (void (*)())android_memset16, 2}, + + {"dmemset32", (void (*)())android_memset32_dumb, 4}, + {"tmemset32", (void (*)())android_memset32_test, 4}, + {"lmemset32", (void (*)())android_memset32, 4}, + + {"cmemset", (void (*)())memset_cmips, 1}, + {"omemset", (void (*)())memset_omips, 1}, + {"lmemset", (void (*)())memset, 1}, + }; +#define NFUNCTIONS (sizeof(functions)/sizeof(functions[0])) + char tag[40]; + int threshold; + + measure_overhead(); + + /* Warm up the page cache */ + memset(arena, 0xff, ARENASIZE); /* use 0xff now to avoid COW later */ + + for (fp = functions; fp < &functions[NFUNCTIONS]; fp++) { + (fp->fn)(arena, 0xffffffff, ARENASIZE); /* one call to get the code into Icache */ + for (pp = params; pp < ¶ms[NPARAMS]; pp++) { + sprintf(tag, "%10s: %7s %4d-%4d", fp->name, pp->type, pp->minbytes, pp->maxbytes); + + /* Set the cycle threshold */ + threshold = pp->maxbytes * 4 * 10; /* reasonable for cycles and ns */ + testone(tag, fp->fn, pp->trials, pp->minbytes, pp->maxbytes, fp->size, threshold); + } + printf ("\n"); + } + + return 0; +} diff --git a/libcutils/tzfile.h b/libcutils/tzfile.h deleted file mode 100644 index 8c70375..0000000 --- a/libcutils/tzfile.h +++ /dev/null @@ -1,180 +0,0 @@ -#ifndef TZFILE_H - -#define TZFILE_H - -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -/* -** This header is for use ONLY with the time conversion code. -** There is no guarantee that it will remain unchanged, -** or that it will remain at all. -** Do NOT copy it to any system include directory. -** Thank you! -*/ - -/* -** ID -*/ - -#ifndef lint -#ifndef NOID -static char tzfilehid[] = "@(#)tzfile.h 8.1"; -#endif /* !defined NOID */ -#endif /* !defined lint */ - -/* -** Information about time zone files. -*/ - -#ifndef TZDIR -#define TZDIR "/usr/share/zoneinfo" /* "/android/usr/share/zoneinfo" */ /* Time zone object file directory */ -#endif /* !defined TZDIR */ - -#ifndef TZDEFAULT -#define TZDEFAULT "localtime" -#endif /* !defined TZDEFAULT */ - -#ifndef TZDEFRULES -#define TZDEFRULES "posixrules" -#endif /* !defined TZDEFRULES */ - -/* -** Each file begins with. . . -*/ - -#define TZ_MAGIC "TZif" - -struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' as of 2005 */ - char tzh_reserved[15]; /* reserved--must be zero */ - char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ - char tzh_leapcnt[4]; /* coded number of leap seconds */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ -}; - -/* -** . . .followed by. . . -** -** tzh_timecnt (char [4])s coded transition times a la time(2) -** tzh_timecnt (unsigned char)s types of local time starting at above -** tzh_typecnt repetitions of -** one (char [4]) coded UTC offset in seconds -** one (unsigned char) used to set tm_isdst -** one (unsigned char) that's an abbreviation list index -** tzh_charcnt (char)s '\0'-terminated zone abbreviations -** tzh_leapcnt repetitions of -** one (char [4]) coded leap second transition times -** one (char [4]) total correction after above -** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition -** time is standard time, if FALSE, -** transition time is wall clock time -** if absent, transition times are -** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition -** time is UTC, if FALSE, -** transition time is local time -** if absent, transition times are -** assumed to be local time -*/ - -/* -** If tzh_version is '2' or greater, the above is followed by a second instance -** of tzhead and a second instance of the data in which each coded transition -** time uses 8 rather than 4 chars, -** then a POSIX-TZ-environment-variable-style string for use in handling -** instants after the last transition time stored in the file -** (with nothing between the newlines if there is no POSIX representation for -** such instants). -*/ - -/* -** In the current implementation, "tzset()" refuses to deal with files that -** exceed any of the limits below. -*/ - -#ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 1200 -#endif /* !defined TZ_MAX_TIMES */ - -#ifndef TZ_MAX_TYPES -#ifndef NOSOLAR -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ -#endif /* !defined NOSOLAR */ -#ifdef NOSOLAR -/* -** Must be at least 14 for Europe/Riga as of Jan 12 1995, -** as noted by Earl Chew. -*/ -#define TZ_MAX_TYPES 20 /* Maximum number of local time types */ -#endif /* !defined NOSOLAR */ -#endif /* !defined TZ_MAX_TYPES */ - -#ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - /* (limited by what unsigned chars can hold) */ -#endif /* !defined TZ_MAX_CHARS */ - -#ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#endif /* !defined TZ_MAX_LEAPS */ - -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - -#define TM_YEAR_BASE 1900 - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY - -#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) - -/* -** Since everything in isleap is modulo 400 (or a factor of 400), we know that -** isleap(y) == isleap(y % 400) -** and so -** isleap(a + b) == isleap((a + b) % 400) -** or -** isleap(a + b) == isleap(a % 400 + b % 400) -** This is true even if % means modulo rather than Fortran remainder -** (which is allowed by C89 but not C99). -** We use this to avoid addition overflow problems. -*/ - -#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) - -#endif /* !defined TZFILE_H */ diff --git a/libcutils/tzstrftime.c b/libcutils/tzstrftime.c deleted file mode 100644 index e37d79a..0000000 --- a/libcutils/tzstrftime.c +++ /dev/null @@ -1,841 +0,0 @@ -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)strftime.c 8.1"; -/* -** Based on the UCB version with the ID appearing below. -** This is ANSIish only when "multibyte character == plain character". -*/ -#endif /* !defined NOID */ -#endif /* !defined lint */ - -#include <time.h> -#include <tzfile.h> -#include <limits.h> -#include <cutils/tztime.h> - -/* -** Copyright (c) 1989 The Regents of the University of California. -** All rights reserved. -** -** Redistribution and use in source and binary forms are permitted -** provided that the above copyright notice and this paragraph are -** duplicated in all such forms and that any documentation, -** advertising materials, and other materials related to such -** distribution and use acknowledge that the software was developed -** by the University of California, Berkeley. The name of the -** University may not be used to endorse or promote products derived -** from this software without specific prior written permission. -** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -*/ - -#ifndef LIBC_SCCS -#ifndef lint -static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; -#endif /* !defined lint */ -#endif /* !defined LIBC_SCCS */ - -#include <ctype.h> - -#define P(x) x - -static char * _add P((const char *, char *, const char *, int)); -static char * _conv P((int, const char *, char *, const char *)); -static char * _fmt P((const char *, const struct tm *, char *, const char *, - int *, const struct strftime_locale *Locale)); -static char * _yconv P((int, int, int, int, char *, const char *, int)); -static char * getformat P((int, char *, char *, char *, char *)); - -extern char * tzname[]; - - - - - -/* from private.h */ - -#ifndef TYPE_BIT -#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) -#endif /* !defined TYPE_BIT */ - -#ifndef TYPE_SIGNED -#define TYPE_SIGNED(type) (((type) -1) < 0) -#endif /* !defined TYPE_SIGNED */ - -#ifndef INT_STRLEN_MAXIMUM -/* - * ** 302 / 1000 is log10(2.0) rounded up. - * ** Subtract one for the sign bit if the type is signed; - * ** add one for integer division truncation; - * ** add one more for a minus sign if the type is signed. - * */ -#define INT_STRLEN_MAXIMUM(type) \ - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ - 1 + TYPE_SIGNED(type)) -#endif /* !defined INT_STRLEN_MAXIMUM */ - -/* end of part from private.h */ - - - - -#ifndef YEAR_2000_NAME -#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" -#endif /* !defined YEAR_2000_NAME */ - -#define IN_NONE 0 -#define IN_SOME 1 -#define IN_THIS 2 -#define IN_ALL 3 - -#define FORCE_LOWER_CASE 0x100 - -size_t -strftime_tz(s, maxsize, format, t, Locale) -char * const s; -const size_t maxsize; -const char * const format; -const struct tm * const t; -const struct strftime_locale *Locale; -{ - char * p; - int warn; - - warn = IN_NONE; - p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, Locale); -#if 0 - if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { - (void) fprintf(stderr, "\n"); - if (format == NULL) - (void) fprintf(stderr, "NULL strftime format "); - else (void) fprintf(stderr, "strftime format \"%s\" ", - format); - (void) fprintf(stderr, "yields only two digits of years in "); - if (warn == IN_SOME) - (void) fprintf(stderr, "some locales"); - else if (warn == IN_THIS) - (void) fprintf(stderr, "the current locale"); - else (void) fprintf(stderr, "all locales"); - (void) fprintf(stderr, "\n"); - } -#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ - if (p == s + maxsize) - return 0; - *p = '\0'; - return p - s; -} - -static char *getformat(int modifier, char *normal, char *underscore, - char *dash, char *zero) { - switch (modifier) { - case '_': - return underscore; - - case '-': - return dash; - - case '0': - return zero; - } - - return normal; -} - -static char * -_fmt(format, t, pt, ptlim, warnp, Locale) -const char * format; -const struct tm * const t; -char * pt; -const char * const ptlim; -int * warnp; -const struct strftime_locale *Locale; -{ - for ( ; *format; ++format) { - if (*format == '%') { - int modifier = 0; -label: - switch (*++format) { - case '\0': - --format; - break; - case 'A': - pt = _add((t->tm_wday < 0 || - t->tm_wday >= DAYSPERWEEK) ? - "?" : Locale->weekday[t->tm_wday], - pt, ptlim, modifier); - continue; - case 'a': - pt = _add((t->tm_wday < 0 || - t->tm_wday >= DAYSPERWEEK) ? - "?" : Locale->wday[t->tm_wday], - pt, ptlim, modifier); - continue; - case 'B': - if (modifier == '-') { - pt = _add((t->tm_mon < 0 || - t->tm_mon >= MONSPERYEAR) ? - "?" : Locale->standalone_month[t->tm_mon], - pt, ptlim, modifier); - } else { - pt = _add((t->tm_mon < 0 || - t->tm_mon >= MONSPERYEAR) ? - "?" : Locale->month[t->tm_mon], - pt, ptlim, modifier); - } - continue; - case 'b': - case 'h': - pt = _add((t->tm_mon < 0 || - t->tm_mon >= MONSPERYEAR) ? - "?" : Locale->mon[t->tm_mon], - pt, ptlim, modifier); - continue; - case 'C': - /* - ** %C used to do a... - ** _fmt("%a %b %e %X %Y", t); - ** ...whereas now POSIX 1003.2 calls for - ** something completely different. - ** (ado, 1993-05-24) - */ - pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, - pt, ptlim, modifier); - continue; - case 'c': - { - int warn2 = IN_SOME; - - pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, Locale); - if (warn2 == IN_ALL) - warn2 = IN_THIS; - if (warn2 > *warnp) - *warnp = warn2; - } - continue; - case 'D': - pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, Locale); - continue; - case 'd': - pt = _conv(t->tm_mday, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'E': - case 'O': - /* - ** C99 locale modifiers. - ** The sequences - ** %Ec %EC %Ex %EX %Ey %EY - ** %Od %oe %OH %OI %Om %OM - ** %OS %Ou %OU %OV %Ow %OW %Oy - ** are supposed to provide alternate - ** representations. - */ - goto label; - case '_': - case '-': - case '0': - case '^': - case '#': - modifier = *format; - goto label; - case 'e': - pt = _conv(t->tm_mday, - getformat(modifier, "%2d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'F': - pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, Locale); - continue; - case 'H': - pt = _conv(t->tm_hour, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'I': - pt = _conv((t->tm_hour % 12) ? - (t->tm_hour % 12) : 12, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'j': - pt = _conv(t->tm_yday + 1, - getformat(modifier, "%03d", "%3d", "%d", "%03d"), - pt, ptlim); - continue; - case 'k': - /* - ** This used to be... - ** _conv(t->tm_hour % 12 ? - ** t->tm_hour % 12 : 12, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbins' - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 1993-05-24) - */ - pt = _conv(t->tm_hour, - getformat(modifier, "%2d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; -#ifdef KITCHEN_SINK - case 'K': - /* - ** After all this time, still unclaimed! - */ - pt = _add("kitchen sink", pt, ptlim, modifier); - continue; -#endif /* defined KITCHEN_SINK */ - case 'l': - /* - ** This used to be... - ** _conv(t->tm_hour, 2, ' '); - ** ...and has been changed to the below to - ** match SunOS 4.1.1 and Arnold Robbin's - ** strftime version 3.0. That is, "%k" and - ** "%l" have been swapped. - ** (ado, 1993-05-24) - */ - pt = _conv((t->tm_hour % 12) ? - (t->tm_hour % 12) : 12, - getformat(modifier, "%2d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'M': - pt = _conv(t->tm_min, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'm': - pt = _conv(t->tm_mon + 1, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'n': - pt = _add("\n", pt, ptlim, modifier); - continue; - case 'p': - pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? - Locale->pm : - Locale->am, - pt, ptlim, modifier); - continue; - case 'P': - pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? - Locale->pm : - Locale->am, - pt, ptlim, FORCE_LOWER_CASE); - continue; - case 'R': - pt = _fmt("%H:%M", t, pt, ptlim, warnp, Locale); - continue; - case 'r': - pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, Locale); - continue; - case 'S': - pt = _conv(t->tm_sec, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 's': - { - struct tm tm; - char buf[INT_STRLEN_MAXIMUM( - time_t) + 1]; - time_t mkt; - - tm = *t; - mkt = mktime(&tm); - if (TYPE_SIGNED(time_t)) - (void) sprintf(buf, "%ld", - (long) mkt); - else (void) sprintf(buf, "%lu", - (unsigned long) mkt); - pt = _add(buf, pt, ptlim, modifier); - } - continue; - case 'T': - pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, Locale); - continue; - case 't': - pt = _add("\t", pt, ptlim, modifier); - continue; - case 'U': - pt = _conv((t->tm_yday + DAYSPERWEEK - - t->tm_wday) / DAYSPERWEEK, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'u': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "ISO 8601: Weekday as a decimal number - ** [1 (Monday) - 7]" - ** (ado, 1993-05-24) - */ - pt = _conv((t->tm_wday == 0) ? - DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); - continue; - case 'V': /* ISO 8601 week number */ - case 'G': /* ISO 8601 year (four digits) */ - case 'g': /* ISO 8601 year (two digits) */ -/* -** From Arnold Robbins' strftime version 3.0: "the week number of the -** year (the first Monday as the first day of week 1) as a decimal number -** (01-53)." -** (ado, 1993-05-24) -** -** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: -** "Week 01 of a year is per definition the first week which has the -** Thursday in this year, which is equivalent to the week which contains -** the fourth day of January. In other words, the first week of a new year -** is the week which has the majority of its days in the new year. Week 01 -** might also contain days from the previous year and the week before week -** 01 of a year is the last week (52 or 53) of the previous year even if -** it contains days from the new year. A week starts with Monday (day 1) -** and ends with Sunday (day 7). For example, the first week of the year -** 1997 lasts from 1996-12-30 to 1997-01-05..." -** (ado, 1996-01-02) -*/ - { - int year; - int base; - int yday; - int wday; - int w; - - year = t->tm_year; - base = TM_YEAR_BASE; - yday = t->tm_yday; - wday = t->tm_wday; - for ( ; ; ) { - int len; - int bot; - int top; - - len = isleap_sum(year, base) ? - DAYSPERLYEAR : - DAYSPERNYEAR; - /* - ** What yday (-3 ... 3) does - ** the ISO year begin on? - */ - bot = ((yday + 11 - wday) % - DAYSPERWEEK) - 3; - /* - ** What yday does the NEXT - ** ISO year begin on? - */ - top = bot - - (len % DAYSPERWEEK); - if (top < -3) - top += DAYSPERWEEK; - top += len; - if (yday >= top) { - ++base; - w = 1; - break; - } - if (yday >= bot) { - w = 1 + ((yday - bot) / - DAYSPERWEEK); - break; - } - --base; - yday += isleap_sum(year, base) ? - DAYSPERLYEAR : - DAYSPERNYEAR; - } -#ifdef XPG4_1994_04_09 - if ((w == 52 && - t->tm_mon == TM_JANUARY) || - (w == 1 && - t->tm_mon == TM_DECEMBER)) - w = 53; -#endif /* defined XPG4_1994_04_09 */ - if (*format == 'V') - pt = _conv(w, - getformat(modifier, - "%02d", - "%2d", - "%d", - "%02d"), - pt, ptlim); - else if (*format == 'g') { - *warnp = IN_ALL; - pt = _yconv(year, base, 0, 1, - pt, ptlim, modifier); - } else pt = _yconv(year, base, 1, 1, - pt, ptlim, modifier); - } - continue; - case 'v': - /* - ** From Arnold Robbins' strftime version 3.0: - ** "date as dd-bbb-YYYY" - ** (ado, 1993-05-24) - */ - pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, Locale); - continue; - case 'W': - pt = _conv((t->tm_yday + DAYSPERWEEK - - (t->tm_wday ? - (t->tm_wday - 1) : - (DAYSPERWEEK - 1))) / DAYSPERWEEK, - getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - continue; - case 'w': - pt = _conv(t->tm_wday, "%d", pt, ptlim); - continue; - case 'X': - pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, Locale); - continue; - case 'x': - { - int warn2 = IN_SOME; - - pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, Locale); - if (warn2 == IN_ALL) - warn2 = IN_THIS; - if (warn2 > *warnp) - *warnp = warn2; - } - continue; - case 'y': - *warnp = IN_ALL; - pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, - pt, ptlim, modifier); - continue; - case 'Y': - pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, - pt, ptlim, modifier); - continue; - case 'Z': -#ifdef TM_ZONE - if (t->TM_ZONE != NULL) - pt = _add(t->TM_ZONE, pt, ptlim, - modifier); - else -#endif /* defined TM_ZONE */ - if (t->tm_isdst >= 0) - pt = _add(tzname[t->tm_isdst != 0], - pt, ptlim, modifier); - /* - ** C99 says that %Z must be replaced by the - ** empty string if the time zone is not - ** determinable. - */ - continue; - case 'z': - { - int diff; - char const * sign; - - if (t->tm_isdst < 0) - continue; -#ifdef TM_GMTOFF - diff = t->TM_GMTOFF; -#else /* !defined TM_GMTOFF */ - /* - ** C99 says that the UTC offset must - ** be computed by looking only at - ** tm_isdst. This requirement is - ** incorrect, since it means the code - ** must rely on magic (in this case - ** altzone and timezone), and the - ** magic might not have the correct - ** offset. Doing things correctly is - ** tricky and requires disobeying C99; - ** see GNU C strftime for details. - ** For now, punt and conform to the - ** standard, even though it's incorrect. - ** - ** C99 says that %z must be replaced by the - ** empty string if the time zone is not - ** determinable, so output nothing if the - ** appropriate variables are not available. - */ - if (t->tm_isdst == 0) -#ifdef USG_COMPAT - diff = -timezone; -#else /* !defined USG_COMPAT */ - continue; -#endif /* !defined USG_COMPAT */ - else -#ifdef ALTZONE - diff = -altzone; -#else /* !defined ALTZONE */ - continue; -#endif /* !defined ALTZONE */ -#endif /* !defined TM_GMTOFF */ - if (diff < 0) { - sign = "-"; - diff = -diff; - } else sign = "+"; - pt = _add(sign, pt, ptlim, modifier); - diff /= SECSPERMIN; - diff = (diff / MINSPERHOUR) * 100 + - (diff % MINSPERHOUR); - pt = _conv(diff, - getformat(modifier, "%04d", - "%4d", "%d", "%04d"), - pt, ptlim); - } - continue; - case '+': - pt = _fmt(Locale->date_fmt, t, pt, ptlim, - warnp, Locale); - continue; - case '%': - /* - ** X311J/88-090 (4.12.3.5): if conversion char is - ** undefined, behavior is undefined. Print out the - ** character itself as printf(3) also does. - */ - default: - break; - } - } - if (pt == ptlim) - break; - *pt++ = *format; - } - return pt; -} - -static char * -_conv(n, format, pt, ptlim) -const int n; -const char * const format; -char * const pt; -const char * const ptlim; -{ - char buf[INT_STRLEN_MAXIMUM(int) + 1]; - - (void) sprintf(buf, format, n); - return _add(buf, pt, ptlim, 0); -} - -static char * -_add(str, pt, ptlim, modifier) -const char * str; -char * pt; -const char * const ptlim; -int modifier; -{ - int c; - - switch (modifier) { - case FORCE_LOWER_CASE: - while (pt < ptlim && (*pt = tolower(*str++)) != '\0') { - ++pt; - } - break; - - case '^': - while (pt < ptlim && (*pt = toupper(*str++)) != '\0') { - ++pt; - } - break; - - case '#': - while (pt < ptlim && (c = *str++) != '\0') { - if (isupper(c)) { - c = tolower(c); - } else if (islower(c)) { - c = toupper(c); - } - *pt = c; - ++pt; - } - - break; - - default: - while (pt < ptlim && (*pt = *str++) != '\0') { - ++pt; - } - } - - return pt; -} - -/* -** POSIX and the C Standard are unclear or inconsistent about -** what %C and %y do if the year is negative or exceeds 9999. -** Use the convention that %C concatenated with %y yields the -** same output as %Y, and that %Y contains at least 4 bytes, -** with more only if necessary. -*/ - -static char * -_yconv(a, b, convert_top, convert_yy, pt, ptlim, modifier) -const int a; -const int b; -const int convert_top; -const int convert_yy; -char * pt; -const char * const ptlim; -int modifier; -{ - register int lead; - register int trail; - -#define DIVISOR 100 - trail = a % DIVISOR + b % DIVISOR; - lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; - trail %= DIVISOR; - if (trail < 0 && lead > 0) { - trail += DIVISOR; - --lead; - } else if (lead < 0 && trail > 0) { - trail -= DIVISOR; - ++lead; - } - if (convert_top) { - if (lead == 0 && trail < 0) - pt = _add("-0", pt, ptlim, modifier); - else pt = _conv(lead, getformat(modifier, "%02d", - "%2d", "%d", "%02d"), - pt, ptlim); - } - if (convert_yy) - pt = _conv(((trail < 0) ? -trail : trail), - getformat(modifier, "%02d", "%2d", "%d", "%02d"), - pt, ptlim); - return pt; -} - -#ifdef LOCALE_HOME -static struct lc_time_T * -_loc P((void)) -{ - static const char locale_home[] = LOCALE_HOME; - static const char lc_time[] = "LC_TIME"; - static char * locale_buf; - - int fd; - int oldsun; /* "...ain't got nothin' to do..." */ - char * lbuf; - char * name; - char * p; - const char ** ap; - const char * plim; - char filename[FILENAME_MAX]; - struct stat st; - size_t namesize; - size_t bufsize; - - /* - ** Use localebuf.mon[0] to signal whether locale is already set up. - */ - if (localebuf.mon[0]) - return &localebuf; - name = setlocale(LC_TIME, (char *) NULL); - if (name == NULL || *name == '\0') - goto no_locale; - /* - ** If the locale name is the same as our cache, use the cache. - */ - lbuf = locale_buf; - if (lbuf != NULL && strcmp(name, lbuf) == 0) { - p = lbuf; - for (ap = (const char **) &localebuf; - ap < (const char **) (&localebuf + 1); - ++ap) - *ap = p += strlen(p) + 1; - return &localebuf; - } - /* - ** Slurp the locale file into the cache. - */ - namesize = strlen(name) + 1; - if (sizeof filename < - ((sizeof locale_home) + namesize + (sizeof lc_time))) - goto no_locale; - oldsun = 0; - (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time); - fd = open(filename, O_RDONLY); - if (fd < 0) { - /* - ** Old Sun systems have a different naming and data convention. - */ - oldsun = 1; - (void) sprintf(filename, "%s/%s/%s", locale_home, - lc_time, name); - fd = open(filename, O_RDONLY); - if (fd < 0) - goto no_locale; - } - if (fstat(fd, &st) != 0) - goto bad_locale; - if (st.st_size <= 0) - goto bad_locale; - bufsize = namesize + st.st_size; - locale_buf = NULL; - lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize); - if (lbuf == NULL) - goto bad_locale; - (void) strcpy(lbuf, name); - p = lbuf + namesize; - plim = p + st.st_size; - if (read(fd, p, (size_t) st.st_size) != st.st_size) - goto bad_lbuf; - if (close(fd) != 0) - goto bad_lbuf; - /* - ** Parse the locale file into localebuf. - */ - if (plim[-1] != '\n') - goto bad_lbuf; - for (ap = (const char **) &localebuf; - ap < (const char **) (&localebuf + 1); - ++ap) { - if (p == plim) - goto bad_lbuf; - *ap = p; - while (*p != '\n') - ++p; - *p++ = '\0'; - } - if (oldsun) { - /* - ** SunOS 4 used an obsolescent format; see localdtconv(3). - ** c_fmt had the ``short format for dates and times together'' - ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale); - ** date_fmt had the ``long format for dates'' - ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale). - ** Discard the latter in favor of the former. - */ - localebuf.date_fmt = localebuf.c_fmt; - } - /* - ** Record the successful parse in the cache. - */ - locale_buf = lbuf; - - return &localebuf; - -bad_lbuf: - free(lbuf); -bad_locale: - (void) close(fd); -no_locale: - localebuf = C_time_locale; - locale_buf = NULL; - return &localebuf; -} -#endif /* defined LOCALE_HOME */ diff --git a/libcutils/tztime.c b/libcutils/tztime.c deleted file mode 100644 index d6448a1..0000000 --- a/libcutils/tztime.c +++ /dev/null @@ -1,1950 +0,0 @@ -/* -** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -#include <stdio.h> - -#ifndef lint -#ifndef NOID -static char elsieid[] = "@(#)localtime.c 8.3"; -#endif /* !defined NOID */ -#endif /* !defined lint */ - -/* -** Leap second handling from Bradley White. -** POSIX-style TZ environment variable handling from Guy Harris. -*/ - -/*LINTLIBRARY*/ - -#include "private.h" -#include "tzfile.h" -#include "fcntl.h" -#include "float.h" /* for FLT_MAX and DBL_MAX */ - -#ifndef TZ_ABBR_MAX_LEN -#define TZ_ABBR_MAX_LEN 16 -#endif /* !defined TZ_ABBR_MAX_LEN */ - -#ifndef TZ_ABBR_CHAR_SET -#define TZ_ABBR_CHAR_SET \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._" -#endif /* !defined TZ_ABBR_CHAR_SET */ - -#ifndef TZ_ABBR_ERR_CHAR -#define TZ_ABBR_ERR_CHAR '_' -#endif /* !defined TZ_ABBR_ERR_CHAR */ - -#define INDEXFILE "/system/usr/share/zoneinfo/zoneinfo.idx" -#define DATAFILE "/system/usr/share/zoneinfo/zoneinfo.dat" -#define NAMELEN 40 -#define INTLEN 4 -#define READLEN (NAMELEN + 3 * INTLEN) - -/* -** SunOS 4.1.1 headers lack O_BINARY. -*/ - -#ifdef O_BINARY -#define OPEN_MODE (O_RDONLY | O_BINARY) -#endif /* defined O_BINARY */ -#ifndef O_BINARY -#define OPEN_MODE O_RDONLY -#endif /* !defined O_BINARY */ - -/* Complex computations to determine the min/max of time_t depending - * on TYPE_BIT / TYPE_SIGNED / TYPE_INTEGRAL. - * These macros cannot be used in pre-processor directives, so we - * let the C compiler do the work, which makes things a bit funky. - */ -static const time_t TIME_T_MAX = - TYPE_INTEGRAL(time_t) ? - ( TYPE_SIGNED(time_t) ? - ~((time_t)1 << (TYPE_BIT(time_t)-1)) - : - ~(time_t)0 - ) - : /* if time_t is a floating point number */ - ( sizeof(time_t) > sizeof(float) ? (time_t)DBL_MAX : (time_t)FLT_MAX ); - -static const time_t TIME_T_MIN = - TYPE_INTEGRAL(time_t) ? - ( TYPE_SIGNED(time_t) ? - ((time_t)1 << (TYPE_BIT(time_t)-1)) - : - 0 - ) - : - ( sizeof(time_t) > sizeof(float) ? (time_t)DBL_MIN : (time_t)FLT_MIN ); - -#ifndef WILDABBR -/* -** Someone might make incorrect use of a time zone abbreviation: -** 1. They might reference tzname[0] before calling tzset (explicitly -** or implicitly). -** 2. They might reference tzname[1] before calling tzset (explicitly -** or implicitly). -** 3. They might reference tzname[1] after setting to a time zone -** in which Daylight Saving Time is never observed. -** 4. They might reference tzname[0] after setting to a time zone -** in which Standard Time is never observed. -** 5. They might reference tm.TM_ZONE after calling offtime. -** What's best to do in the above cases is open to debate; -** for now, we just set things up so that in any of the five cases -** WILDABBR is used. Another possibility: initialize tzname[0] to the -** string "tzname[0] used before set", and similarly for the other cases. -** And another: initialize tzname[0] to "ERA", with an explanation in the -** manual page of what this "time zone abbreviation" means (doing this so -** that tzname[0] has the "normal" length of three characters). -*/ -#define WILDABBR " " -#endif /* !defined WILDABBR */ - -static char wildabbr[] = WILDABBR; - -static const char gmt[] = "GMT"; - -/* -** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. -** We default to US rules as of 1999-08-17. -** POSIX 1003.1 section 8.1.1 says that the default DST rules are -** implementation dependent; for historical reasons, US rules are a -** common default. -*/ -#ifndef TZDEFRULESTRING -#define TZDEFRULESTRING ",M4.1.0,M10.5.0" -#endif /* !defined TZDEFDST */ - -struct ttinfo { /* time type information */ - long tt_gmtoff; /* UTC offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ - int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* TRUE if transition is std time */ - int tt_ttisgmt; /* TRUE if transition is UTC */ -}; - -struct lsinfo { /* leap second information */ - time_t ls_trans; /* transition time */ - long ls_corr; /* correction to apply */ -}; - -#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) - -#ifdef TZNAME_MAX -#define MY_TZNAME_MAX TZNAME_MAX -#endif /* defined TZNAME_MAX */ -#ifndef TZNAME_MAX -#define MY_TZNAME_MAX 255 -#endif /* !defined TZNAME_MAX */ - -struct state { - int leapcnt; - int timecnt; - int typecnt; - int charcnt; - int goback; - int goahead; - time_t ats[TZ_MAX_TIMES]; - unsigned char types[TZ_MAX_TIMES]; - struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), - (2 * (MY_TZNAME_MAX + 1)))]; - struct lsinfo lsis[TZ_MAX_LEAPS]; -}; - -struct rule { - int r_type; /* type of rule--see below */ - int r_day; /* day number of rule */ - int r_week; /* week number of rule */ - int r_mon; /* month number of rule */ - long r_time; /* transition time of rule */ -}; - -#define JULIAN_DAY 0 /* Jn - Julian day */ -#define DAY_OF_YEAR 1 /* n - day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ - -/* -** Prototypes for static functions. -*/ - -static long detzcode P((const char * codep)); -static time_t detzcode64 P((const char * codep)); -static int differ_by_repeat P((time_t t1, time_t t0)); -static const char * getzname P((const char * strp)); -static const char * getqzname P((const char * strp, const int delim)); -static const char * getnum P((const char * strp, int * nump, int min, - int max)); -static const char * getsecs P((const char * strp, long * secsp)); -static const char * getoffset P((const char * strp, long * offsetp)); -static const char * getrule P((const char * strp, struct rule * rulep)); -static void gmtload P((struct state * sp)); -static struct tm * gmtsub P((const time_t * timep, long offset, - struct tm * tmp)); -static struct tm * localsub P((const time_t * timep, long offset, - struct tm * tmp, const struct state *sp)); -static int increment_overflow P((int * number, int delta)); -static int leaps_thru_end_of P((int y)); -static int long_increment_overflow P((long * number, int delta)); -static int long_normalize_overflow P((long * tensptr, - int * unitsptr, int base)); -static int normalize_overflow P((int * tensptr, int * unitsptr, - int base)); -static void settzname P((void)); -static time_t time1 P((struct tm * tmp, - struct tm * (*funcp) P((const time_t *, - long, struct tm *, const struct state* sp)), - long offset, const struct state * sp)); -static time_t time2 P((struct tm *tmp, - struct tm * (*funcp) P((const time_t *, - long, struct tm*, const struct state* sp)), - long offset, int * okayp, const struct state * sp)); -static time_t time2sub P((struct tm *tmp, - struct tm * (*funcp) P((const time_t*, long, struct tm*,const struct state *sp)), - long offset, int * okayp, int do_norm_secs, - const struct state *sp)); -static struct tm * timesub P((const time_t * timep, long offset, - const struct state * sp, struct tm * tmp)); -static int tmcomp P((const struct tm * atmp, - const struct tm * btmp)); -static time_t transtime P((time_t janfirst, int year, - const struct rule * rulep, long offset)); -static int tzload P((const char * name, struct state * sp, - int doextend)); -static int tzload_uncached P((const char * name, struct state * sp, - int doextend)); -static int tzparse P((const char * name, struct state * sp, - int lastditch)); - -#ifdef ALL_STATE -static struct state * gmtptr; -#endif /* defined ALL_STATE */ - -#ifndef ALL_STATE -static struct state gmtmem; -#define gmtptr (&gmtmem) -#endif /* State Farm */ - -#define CACHE_COUNT 4 -static char * g_cacheNames[CACHE_COUNT] = {0,0}; -static struct state g_cacheStates[CACHE_COUNT]; -static int g_lastCache = 0; -static struct state g_utc; -unsigned char g_utcSet = 0; - - -#ifndef TZ_STRLEN_MAX -#define TZ_STRLEN_MAX 255 -#endif /* !defined TZ_STRLEN_MAX */ - -static char lcl_TZname[TZ_STRLEN_MAX + 1]; -static int lcl_is_set; -static int gmt_is_set; - -char * tzname[2] = { - wildabbr, - wildabbr -}; - -/* -** Section 4.12.3 of X3.159-1989 requires that -** Except for the strftime function, these functions [asctime, -** ctime, gmtime, localtime] return values in one of two static -** objects: a broken-down time structure and an array of char. -** Thanks to Paul Eggert for noting this. -*/ - -static struct tm tm; - -#ifdef USG_COMPAT -time_t timezone = 0; -int daylight = 0; -#endif /* defined USG_COMPAT */ - -#ifdef ALTZONE -time_t altzone = 0; -#endif /* defined ALTZONE */ - -static long -detzcode(codep) -const char * const codep; -{ - register long result; - register int i; - - result = (codep[0] & 0x80) ? ~0L : 0; - for (i = 0; i < 4; ++i) - result = (result << 8) | (codep[i] & 0xff); - return result; -} - -static time_t -detzcode64(codep) -const char * const codep; -{ - register time_t result; - register int i; - - result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0; - for (i = 0; i < 8; ++i) - result = result * 256 + (codep[i] & 0xff); - return result; -} - -static int -differ_by_repeat(t1, t0) -const time_t t1; -const time_t t0; -{ - if (TYPE_INTEGRAL(time_t) && - TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) - return 0; - return t1 - t0 == SECSPERREPEAT; -} - -static int toint(unsigned char *s) { - return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; -} - -static int -tzload(const char *name, struct state * const sp, const int doextend) -{ - if (name) { - int i, err; - if (0 == strcmp(name, "UTC")) { - if (!g_utcSet) { - tzload_uncached(name, &g_utc, 1); - g_utcSet = 1; - } - //printf("tzload: utc\n"); - *sp = g_utc; - return 0; - } - for (i=0; i<CACHE_COUNT; i++) { - if (g_cacheNames[i] && 0 == strcmp(name, g_cacheNames[i])) { - *sp = g_cacheStates[i]; - //printf("tzload: hit: %s\n", name); - return 0; - } - } - //printf("tzload: miss: %s\n", name); - g_lastCache++; - if (g_lastCache >= CACHE_COUNT) { - g_lastCache = 0; - } - i = g_lastCache; - if (g_cacheNames[i]) { - free(g_cacheNames[i]); - } - err = tzload_uncached(name, &(g_cacheStates[i]), 1); - if (err == 0) { - g_cacheNames[i] = strdup(name); - *sp = g_cacheStates[i]; - return 0; - } else { - g_cacheNames[i] = NULL; - return err; - } - } - return tzload_uncached(name, sp, doextend); -} - -static int -tzload_uncached(name, sp, doextend) -register const char * name; -register struct state * const sp; -register const int doextend; -{ - register const char * p; - register int i; - register int fid; - register int stored; - register int nread; - union { - struct tzhead tzhead; - char buf[2 * sizeof(struct tzhead) + - 2 * sizeof *sp + - 4 * TZ_MAX_TIMES]; - } u; - int toread = sizeof u.buf; - - if (name == NULL && (name = TZDEFAULT) == NULL) - return -1; - { - register int doaccess; - /* - ** Section 4.9.1 of the C standard says that - ** "FILENAME_MAX expands to an integral constant expression - ** that is the size needed for an array of char large enough - ** to hold the longest file name string that the implementation - ** guarantees can be opened." - */ - char fullname[FILENAME_MAX + 1]; - const char *origname = name; - - if (name[0] == ':') - ++name; - doaccess = name[0] == '/'; - if (!doaccess) { - if ((p = TZDIR) == NULL) - return -1; - if ((strlen(p) + strlen(name) + 1) >= sizeof fullname) - return -1; - (void) strcpy(fullname, p); - (void) strcat(fullname, "/"); - (void) strcat(fullname, name); - /* - ** Set doaccess if '.' (as in "../") shows up in name. - */ - if (strchr(name, '.') != NULL) - doaccess = TRUE; - name = fullname; - } - if (doaccess && access(name, R_OK) != 0) - return -1; - if ((fid = open(name, OPEN_MODE)) == -1) { - char buf[READLEN]; - char name[NAMELEN + 1]; - int fidix = open(INDEXFILE, OPEN_MODE); - int off = -1; - - if (fidix < 0) { - return -1; - } - - while (read(fidix, buf, sizeof(buf)) == sizeof(buf)) { - memcpy(name, buf, NAMELEN); - name[NAMELEN] = '\0'; - - if (strcmp(name, origname) == 0) { - off = toint((unsigned char *) buf + NAMELEN); - toread = toint((unsigned char *) buf + NAMELEN + INTLEN); - break; - } - } - - close(fidix); - - if (off < 0) - return -1; - - fid = open(DATAFILE, OPEN_MODE); - - if (fid < 0) { - return -1; - } - - if (lseek(fid, off, SEEK_SET) < 0) { - return -1; - } - } - } - nread = read(fid, u.buf, toread); - if (close(fid) < 0 || nread <= 0) - return -1; - for (stored = 4; stored <= 8; stored *= 2) { - int ttisstdcnt; - int ttisgmtcnt; - - ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); - ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); - sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); - sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); - sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); - sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); - p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; - if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || - sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || - sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || - sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS || - (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || - (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) - return -1; - if (nread - (p - u.buf) < - sp->timecnt * stored + /* ats */ - sp->timecnt + /* types */ - sp->typecnt * 6 + /* ttinfos */ - sp->charcnt + /* chars */ - sp->leapcnt * (stored + 4) + /* lsinfos */ - ttisstdcnt + /* ttisstds */ - ttisgmtcnt) /* ttisgmts */ - return -1; - for (i = 0; i < sp->timecnt; ++i) { - sp->ats[i] = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - } - for (i = 0; i < sp->timecnt; ++i) { - sp->types[i] = (unsigned char) *p++; - if (sp->types[i] >= sp->typecnt) - return -1; - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - ttisp->tt_gmtoff = detzcode(p); - p += 4; - ttisp->tt_isdst = (unsigned char) *p++; - if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) - return -1; - ttisp->tt_abbrind = (unsigned char) *p++; - if (ttisp->tt_abbrind < 0 || - ttisp->tt_abbrind > sp->charcnt) - return -1; - } - for (i = 0; i < sp->charcnt; ++i) - sp->chars[i] = *p++; - sp->chars[i] = '\0'; /* ensure '\0' at end */ - for (i = 0; i < sp->leapcnt; ++i) { - register struct lsinfo * lsisp; - - lsisp = &sp->lsis[i]; - lsisp->ls_trans = (stored == 4) ? - detzcode(p) : detzcode64(p); - p += stored; - lsisp->ls_corr = detzcode(p); - p += 4; - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisstdcnt == 0) - ttisp->tt_ttisstd = FALSE; - else { - ttisp->tt_ttisstd = *p++; - if (ttisp->tt_ttisstd != TRUE && - ttisp->tt_ttisstd != FALSE) - return -1; - } - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisgmtcnt == 0) - ttisp->tt_ttisgmt = FALSE; - else { - ttisp->tt_ttisgmt = *p++; - if (ttisp->tt_ttisgmt != TRUE && - ttisp->tt_ttisgmt != FALSE) - return -1; - } - } - /* - ** Out-of-sort ats should mean we're running on a - ** signed time_t system but using a data file with - ** unsigned values (or vice versa). - */ - for (i = 0; i < sp->timecnt - 2; ++i) - if (sp->ats[i] > sp->ats[i + 1]) { - ++i; - if (TYPE_SIGNED(time_t)) { - /* - ** Ignore the end (easy). - */ - sp->timecnt = i; - } else { - /* - ** Ignore the beginning (harder). - */ - register int j; - - for (j = 0; j + i < sp->timecnt; ++j) { - sp->ats[j] = sp->ats[j + i]; - sp->types[j] = sp->types[j + i]; - } - sp->timecnt = j; - } - break; - } - /* - ** If this is an old file, we're done. - */ - if (u.tzhead.tzh_version[0] == '\0') - break; - nread -= p - u.buf; - for (i = 0; i < nread; ++i) - u.buf[i] = p[i]; - /* - ** If this is a narrow integer time_t system, we're done. - */ - if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t)) - break; - } - if (doextend && nread > 2 && - u.buf[0] == '\n' && u.buf[nread - 1] == '\n' && - sp->typecnt + 2 <= TZ_MAX_TYPES) { - struct state ts; - register int result; - - u.buf[nread - 1] = '\0'; - result = tzparse(&u.buf[1], &ts, FALSE); - if (result == 0 && ts.typecnt == 2 && - sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) { - for (i = 0; i < 2; ++i) - ts.ttis[i].tt_abbrind += - sp->charcnt; - for (i = 0; i < ts.charcnt; ++i) - sp->chars[sp->charcnt++] = - ts.chars[i]; - i = 0; - while (i < ts.timecnt && - ts.ats[i] <= - sp->ats[sp->timecnt - 1]) - ++i; - while (i < ts.timecnt && - sp->timecnt < TZ_MAX_TIMES) { - sp->ats[sp->timecnt] = - ts.ats[i]; - sp->types[sp->timecnt] = - sp->typecnt + - ts.types[i]; - ++sp->timecnt; - ++i; - } - sp->ttis[sp->typecnt++] = ts.ttis[0]; - sp->ttis[sp->typecnt++] = ts.ttis[1]; - } - } - i = 2 * YEARSPERREPEAT; - sp->goback = sp->goahead = sp->timecnt > i; - sp->goback &= sp->types[i] == sp->types[0] && - differ_by_repeat(sp->ats[i], sp->ats[0]); - sp->goahead &= - sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] && - differ_by_repeat(sp->ats[sp->timecnt - 1], - sp->ats[sp->timecnt - 1 - i]); - return 0; -} - -static const int mon_lengths[2][MONSPERYEAR] = { - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, - { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } -}; - -static const int year_lengths[2] = { - DAYSPERNYEAR, DAYSPERLYEAR -}; - -/* -** Given a pointer into a time zone string, scan until a character that is not -** a valid character in a zone name is found. Return a pointer to that -** character. -*/ - -static const char * -getzname(strp) -register const char * strp; -{ - register char c; - - while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && - c != '+') - ++strp; - return strp; -} - -/* -** Given a pointer into an extended time zone string, scan until the ending -** delimiter of the zone name is located. Return a pointer to the delimiter. -** -** As with getzname above, the legal character set is actually quite -** restricted, with other characters producing undefined results. -** We don't do any checking here; checking is done later in common-case code. -*/ - -static const char * -getqzname(register const char *strp, const int delim) -{ - register int c; - - while ((c = *strp) != '\0' && c != delim) - ++strp; - return strp; -} - -/* -** Given a pointer into a time zone string, extract a number from that string. -** Check that the number is within a specified range; if it is not, return -** NULL. -** Otherwise, return a pointer to the first character not part of the number. -*/ - -static const char * -getnum(strp, nump, min, max) -register const char * strp; -int * const nump; -const int min; -const int max; -{ - register char c; - register int num; - - if (strp == NULL || !is_digit(c = *strp)) - return NULL; - num = 0; - do { - num = num * 10 + (c - '0'); - if (num > max) - return NULL; /* illegal value */ - c = *++strp; - } while (is_digit(c)); - if (num < min) - return NULL; /* illegal value */ - *nump = num; - return strp; -} - -/* -** Given a pointer into a time zone string, extract a number of seconds, -** in hh[:mm[:ss]] form, from the string. -** If any error occurs, return NULL. -** Otherwise, return a pointer to the first character not part of the number -** of seconds. -*/ - -static const char * -getsecs(strp, secsp) -register const char * strp; -long * const secsp; -{ - int num; - - /* - ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like - ** "M10.4.6/26", which does not conform to Posix, - ** but which specifies the equivalent of - ** ``02:00 on the first Sunday on or after 23 Oct''. - */ - strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); - if (strp == NULL) - return NULL; - *secsp = num * (long) SECSPERHOUR; - if (*strp == ':') { - ++strp; - strp = getnum(strp, &num, 0, MINSPERHOUR - 1); - if (strp == NULL) - return NULL; - *secsp += num * SECSPERMIN; - if (*strp == ':') { - ++strp; - /* `SECSPERMIN' allows for leap seconds. */ - strp = getnum(strp, &num, 0, SECSPERMIN); - if (strp == NULL) - return NULL; - *secsp += num; - } - } - return strp; -} - -/* -** Given a pointer into a time zone string, extract an offset, in -** [+-]hh[:mm[:ss]] form, from the string. -** If any error occurs, return NULL. -** Otherwise, return a pointer to the first character not part of the time. -*/ - -static const char * -getoffset(strp, offsetp) -register const char * strp; -long * const offsetp; -{ - register int neg = 0; - - if (*strp == '-') { - neg = 1; - ++strp; - } else if (*strp == '+') - ++strp; - strp = getsecs(strp, offsetp); - if (strp == NULL) - return NULL; /* illegal time */ - if (neg) - *offsetp = -*offsetp; - return strp; -} - -/* -** Given a pointer into a time zone string, extract a rule in the form -** date[/time]. See POSIX section 8 for the format of "date" and "time". -** If a valid rule is not found, return NULL. -** Otherwise, return a pointer to the first character not part of the rule. -*/ - -static const char * -getrule(strp, rulep) -const char * strp; -register struct rule * const rulep; -{ - if (*strp == 'J') { - /* - ** Julian day. - */ - rulep->r_type = JULIAN_DAY; - ++strp; - strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); - } else if (*strp == 'M') { - /* - ** Month, week, day. - */ - rulep->r_type = MONTH_NTH_DAY_OF_WEEK; - ++strp; - strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); - if (strp == NULL) - return NULL; - if (*strp++ != '.') - return NULL; - strp = getnum(strp, &rulep->r_week, 1, 5); - if (strp == NULL) - return NULL; - if (*strp++ != '.') - return NULL; - strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); - } else if (is_digit(*strp)) { - /* - ** Day of year. - */ - rulep->r_type = DAY_OF_YEAR; - strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); - } else return NULL; /* invalid format */ - if (strp == NULL) - return NULL; - if (*strp == '/') { - /* - ** Time specified. - */ - ++strp; - strp = getsecs(strp, &rulep->r_time); - } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ - return strp; -} - -/* -** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the -** year, a rule, and the offset from UTC at the time that rule takes effect, -** calculate the Epoch-relative time that rule takes effect. -*/ - -static time_t -transtime(janfirst, year, rulep, offset) -const time_t janfirst; -const int year; -register const struct rule * const rulep; -const long offset; -{ - register int leapyear; - register time_t value; - register int i; - int d, m1, yy0, yy1, yy2, dow; - - INITIALIZE(value); - leapyear = isleap(year); - switch (rulep->r_type) { - - case JULIAN_DAY: - /* - ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap - ** years. - ** In non-leap years, or if the day number is 59 or less, just - ** add SECSPERDAY times the day number-1 to the time of - ** January 1, midnight, to get the day. - */ - value = janfirst + (rulep->r_day - 1) * SECSPERDAY; - if (leapyear && rulep->r_day >= 60) - value += SECSPERDAY; - break; - - case DAY_OF_YEAR: - /* - ** n - day of year. - ** Just add SECSPERDAY times the day number to the time of - ** January 1, midnight, to get the day. - */ - value = janfirst + rulep->r_day * SECSPERDAY; - break; - - case MONTH_NTH_DAY_OF_WEEK: - /* - ** Mm.n.d - nth "dth day" of month m. - */ - value = janfirst; - for (i = 0; i < rulep->r_mon - 1; ++i) - value += mon_lengths[leapyear][i] * SECSPERDAY; - - /* - ** Use Zeller's Congruence to get day-of-week of first day of - ** month. - */ - m1 = (rulep->r_mon + 9) % 12 + 1; - yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; - yy1 = yy0 / 100; - yy2 = yy0 % 100; - dow = ((26 * m1 - 2) / 10 + - 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; - if (dow < 0) - dow += DAYSPERWEEK; - - /* - ** "dow" is the day-of-week of the first day of the month. Get - ** the day-of-month (zero-origin) of the first "dow" day of the - ** month. - */ - d = rulep->r_day - dow; - if (d < 0) - d += DAYSPERWEEK; - for (i = 1; i < rulep->r_week; ++i) { - if (d + DAYSPERWEEK >= - mon_lengths[leapyear][rulep->r_mon - 1]) - break; - d += DAYSPERWEEK; - } - - /* - ** "d" is the day-of-month (zero-origin) of the day we want. - */ - value += d * SECSPERDAY; - break; - } - - /* - ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in - ** question. To get the Epoch-relative time of the specified local - ** time on that day, add the transition time and the current offset - ** from UTC. - */ - return value + rulep->r_time + offset; -} - -/* -** Given a POSIX section 8-style TZ string, fill in the rule tables as -** appropriate. -*/ - -static int -tzparse(name, sp, lastditch) -const char * name; -register struct state * const sp; -const int lastditch; -{ - const char * stdname; - const char * dstname; - size_t stdlen; - size_t dstlen; - long stdoffset; - long dstoffset; - register time_t * atp; - register unsigned char * typep; - register char * cp; - register int load_result; - - INITIALIZE(dstname); - stdname = name; - if (lastditch) { - stdlen = strlen(name); /* length of standard zone name */ - name += stdlen; - if (stdlen >= sizeof sp->chars) - stdlen = (sizeof sp->chars) - 1; - stdoffset = 0; - } else { - if (*name == '<') { - name++; - stdname = name; - name = getqzname(name, '>'); - if (*name != '>') - return (-1); - stdlen = name - stdname; - name++; - } else { - name = getzname(name); - stdlen = name - stdname; - } - if (*name == '\0') - return -1; - name = getoffset(name, &stdoffset); - if (name == NULL) - return -1; - } - load_result = tzload(TZDEFRULES, sp, FALSE); - if (load_result != 0) - sp->leapcnt = 0; /* so, we're off a little */ - sp->timecnt = 0; - if (*name != '\0') { - if (*name == '<') { - dstname = ++name; - name = getqzname(name, '>'); - if (*name != '>') - return -1; - dstlen = name - dstname; - name++; - } else { - dstname = name; - name = getzname(name); - dstlen = name - dstname; /* length of DST zone name */ - } - if (*name != '\0' && *name != ',' && *name != ';') { - name = getoffset(name, &dstoffset); - if (name == NULL) - return -1; - } else dstoffset = stdoffset - SECSPERHOUR; - if (*name == '\0' && load_result != 0) - name = TZDEFRULESTRING; - if (*name == ',' || *name == ';') { - struct rule start; - struct rule end; - register int year; - register time_t janfirst; - time_t starttime; - time_t endtime; - - ++name; - if ((name = getrule(name, &start)) == NULL) - return -1; - if (*name++ != ',') - return -1; - if ((name = getrule(name, &end)) == NULL) - return -1; - if (*name != '\0') - return -1; - sp->typecnt = 2; /* standard time and DST */ - /* - ** Two transitions per year, from EPOCH_YEAR forward. - */ - sp->ttis[0].tt_gmtoff = -dstoffset; - sp->ttis[0].tt_isdst = 1; - sp->ttis[0].tt_abbrind = stdlen + 1; - sp->ttis[1].tt_gmtoff = -stdoffset; - sp->ttis[1].tt_isdst = 0; - sp->ttis[1].tt_abbrind = 0; - atp = sp->ats; - typep = sp->types; - janfirst = 0; - for (year = EPOCH_YEAR; - sp->timecnt + 2 <= TZ_MAX_TIMES; - ++year) { - time_t newfirst; - - starttime = transtime(janfirst, year, &start, - stdoffset); - endtime = transtime(janfirst, year, &end, - dstoffset); - if (starttime > endtime) { - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - } else { - *atp++ = starttime; - *typep++ = 0; /* DST begins */ - *atp++ = endtime; - *typep++ = 1; /* DST ends */ - } - sp->timecnt += 2; - newfirst = janfirst; - newfirst += year_lengths[isleap(year)] * - SECSPERDAY; - if (newfirst <= janfirst) - break; - janfirst = newfirst; - } - } else { - register long theirstdoffset; - register long theirdstoffset; - register long theiroffset; - register int isdst; - register int i; - register int j; - - if (*name != '\0') - return -1; - /* - ** Initial values of theirstdoffset and theirdstoffset. - */ - theirstdoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (!sp->ttis[j].tt_isdst) { - theirstdoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - theirdstoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (sp->ttis[j].tt_isdst) { - theirdstoffset = - -sp->ttis[j].tt_gmtoff; - break; - } - } - /* - ** Initially we're assumed to be in standard time. - */ - isdst = FALSE; - theiroffset = theirstdoffset; - /* - ** Now juggle transition times and types - ** tracking offsets as you do. - */ - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - sp->types[i] = sp->ttis[j].tt_isdst; - if (sp->ttis[j].tt_ttisgmt) { - /* No adjustment to transition time */ - } else { - /* - ** If summer time is in effect, and the - ** transition time was not specified as - ** standard time, add the summer time - ** offset to the transition time; - ** otherwise, add the standard time - ** offset to the transition time. - */ - /* - ** Transitions from DST to DDST - ** will effectively disappear since - ** POSIX provides for only one DST - ** offset. - */ - if (isdst && !sp->ttis[j].tt_ttisstd) { - sp->ats[i] += dstoffset - - theirdstoffset; - } else { - sp->ats[i] += stdoffset - - theirstdoffset; - } - } - theiroffset = -sp->ttis[j].tt_gmtoff; - if (sp->ttis[j].tt_isdst) - theirdstoffset = theiroffset; - else theirstdoffset = theiroffset; - } - /* - ** Finally, fill in ttis. - ** ttisstd and ttisgmt need not be handled. - */ - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = FALSE; - sp->ttis[0].tt_abbrind = 0; - sp->ttis[1].tt_gmtoff = -dstoffset; - sp->ttis[1].tt_isdst = TRUE; - sp->ttis[1].tt_abbrind = stdlen + 1; - sp->typecnt = 2; - } - } else { - dstlen = 0; - sp->typecnt = 1; /* only standard time */ - sp->timecnt = 0; - sp->ttis[0].tt_gmtoff = -stdoffset; - sp->ttis[0].tt_isdst = 0; - sp->ttis[0].tt_abbrind = 0; - } - sp->charcnt = stdlen + 1; - if (dstlen != 0) - sp->charcnt += dstlen + 1; - if ((size_t) sp->charcnt > sizeof sp->chars) - return -1; - cp = sp->chars; - (void) strncpy(cp, stdname, stdlen); - cp += stdlen; - *cp++ = '\0'; - if (dstlen != 0) { - (void) strncpy(cp, dstname, dstlen); - *(cp + dstlen) = '\0'; - } - return 0; -} - -static void -gmtload(sp) -struct state * const sp; -{ - if (tzload(gmt, sp, TRUE) != 0) - (void) tzparse(gmt, sp, TRUE); -} - -/* -** The easy way to behave "as if no library function calls" localtime -** is to not call it--so we drop its guts into "localsub", which can be -** freely called. (And no, the PANS doesn't require the above behavior-- -** but it *is* desirable.) -** -** The unused offset argument is for the benefit of mktime variants. -*/ - -/*ARGSUSED*/ -static struct tm * -localsub(timep, offset, tmp, sp) -const time_t * const timep; -const long offset; -struct tm * const tmp; -const struct state * sp; -{ - register const struct ttinfo * ttisp; - register int i; - register struct tm * result; - const time_t t = *timep; - -#ifdef ALL_STATE - if (sp == NULL) - return gmtsub(timep, offset, tmp); -#endif /* defined ALL_STATE */ - if ((sp->goback && t < sp->ats[0]) || - (sp->goahead && t > sp->ats[sp->timecnt - 1])) { - time_t newt = t; - register time_t seconds; - register time_t tcycles; - register int_fast64_t icycles; - - if (t < sp->ats[0]) - seconds = sp->ats[0] - t; - else seconds = t - sp->ats[sp->timecnt - 1]; - --seconds; - tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; - ++tcycles; - icycles = tcycles; - if (tcycles - icycles >= 1 || icycles - tcycles >= 1) - return NULL; - seconds = icycles; - seconds *= YEARSPERREPEAT; - seconds *= AVGSECSPERYEAR; - if (t < sp->ats[0]) - newt += seconds; - else newt -= seconds; - if (newt < sp->ats[0] || - newt > sp->ats[sp->timecnt - 1]) - return NULL; /* "cannot happen" */ - result = localsub(&newt, offset, tmp, sp); - if (result == tmp) { - register time_t newy; - - newy = tmp->tm_year; - if (t < sp->ats[0]) - newy -= icycles * YEARSPERREPEAT; - else newy += icycles * YEARSPERREPEAT; - tmp->tm_year = newy; - if (tmp->tm_year != newy) - return NULL; - } - return result; - } - if (sp->timecnt == 0 || t < sp->ats[0]) { - i = 0; - while (sp->ttis[i].tt_isdst) - if (++i >= sp->typecnt) { - i = 0; - break; - } - } else { - register int lo = 1; - register int hi = sp->timecnt; - - while (lo < hi) { - register int mid = (lo + hi) >> 1; - - if (t < sp->ats[mid]) - hi = mid; - else lo = mid + 1; - } - i = (int) sp->types[lo - 1]; - } - ttisp = &sp->ttis[i]; - /* - ** To get (wrong) behavior that's compatible with System V Release 2.0 - ** you'd replace the statement below with - ** t += ttisp->tt_gmtoff; - ** timesub(&t, 0L, sp, tmp); - */ - result = timesub(&t, ttisp->tt_gmtoff, sp, tmp); - tmp->tm_isdst = ttisp->tt_isdst; -#ifdef HAVE_TM_GMTOFF - tmp->tm_gmtoff = ttisp->tt_gmtoff; -#endif - tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind]; -#ifdef TM_ZONE - tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind]; -#endif /* defined TM_ZONE */ - return result; -} - - -// ============================================================================ -#if 0 -struct tm * -localtime(timep) -const time_t * const timep; -{ - tzset(); - return localsub(timep, 0L, &tm); -} -#endif - -/* -** Re-entrant version of localtime. -*/ - -// ============================================================================ -void -localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz) -{ - struct state st; - if (tzload(tz, &st, TRUE) != 0) { - // not sure what's best here, but for now, we fall back to gmt - gmtload(&st); - } - - localsub(timep, 0L, tmp, &st); -} - -/* -** gmtsub is to gmtime as localsub is to localtime. -*/ - -static struct tm * -gmtsub(timep, offset, tmp) -const time_t * const timep; -const long offset; -struct tm * const tmp; -{ - register struct tm * result; - - if (!gmt_is_set) { - gmt_is_set = TRUE; -#ifdef ALL_STATE - gmtptr = (struct state *) malloc(sizeof *gmtptr); - if (gmtptr != NULL) -#endif /* defined ALL_STATE */ - gmtload(gmtptr); - } - result = timesub(timep, offset, gmtptr, tmp); -#ifdef TM_ZONE - /* - ** Could get fancy here and deliver something such as - ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, - ** but this is no time for a treasure hunt. - */ - if (offset != 0) - tmp->TM_ZONE = wildabbr; - else { -#ifdef ALL_STATE - if (gmtptr == NULL) - tmp->TM_ZONE = gmt; - else tmp->TM_ZONE = gmtptr->chars; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - tmp->TM_ZONE = gmtptr->chars; -#endif /* State Farm */ - } -#endif /* defined TM_ZONE */ - return result; -} - -// ============================================================================ -#if 0 -struct tm * -gmtime(timep) -const time_t * const timep; -{ - return gmtsub(timep, 0L, &tm); -} -#endif - -/* -* Re-entrant version of gmtime. -*/ - -// ============================================================================ -#if 0 -struct tm * -gmtime_r(timep, tmp) -const time_t * const timep; -struct tm * tmp; -{ - return gmtsub(timep, 0L, tmp); -} -#endif - -#ifdef STD_INSPIRED - -// ============================================================================ -#if 0 -struct tm * -offtime(timep, offset) -const time_t * const timep; -const long offset; -{ - return gmtsub(timep, offset, &tm); -} -#endif - -#endif /* defined STD_INSPIRED */ - -/* -** Return the number of leap years through the end of the given year -** where, to make the math easy, the answer for year zero is defined as zero. -*/ - -static int -leaps_thru_end_of(y) -register const int y; -{ - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : - -(leaps_thru_end_of(-(y + 1)) + 1); -} - -static struct tm * -timesub(timep, offset, sp, tmp) -const time_t * const timep; -const long offset; -register const struct state * const sp; -register struct tm * const tmp; -{ - register const struct lsinfo * lp; - register time_t tdays; - register int idays; /* unsigned would be so 2003 */ - register long rem; - int y; - register const int * ip; - register long corr; - register int hit; - register int i; - - corr = 0; - hit = 0; -#ifdef ALL_STATE - i = (sp == NULL) ? 0 : sp->leapcnt; -#endif /* defined ALL_STATE */ -#ifndef ALL_STATE - i = sp->leapcnt; -#endif /* State Farm */ - while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) { - hit = ((i == 0 && lp->ls_corr > 0) || - lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && - sp->lsis[i].ls_trans == - sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == - sp->lsis[i - 1].ls_corr + 1) { - ++hit; - --i; - } - } - corr = lp->ls_corr; - break; - } - } - y = EPOCH_YEAR; - tdays = *timep / SECSPERDAY; - rem = *timep - tdays * SECSPERDAY; - while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { - int newy; - register time_t tdelta; - register int idelta; - register int leapdays; - - tdelta = tdays / DAYSPERLYEAR; - idelta = tdelta; - if (tdelta - idelta >= 1 || idelta - tdelta >= 1) - return NULL; - if (idelta == 0) - idelta = (tdays < 0) ? -1 : 1; - newy = y; - if (increment_overflow(&newy, idelta)) - return NULL; - leapdays = leaps_thru_end_of(newy - 1) - - leaps_thru_end_of(y - 1); - tdays -= ((time_t) newy - y) * DAYSPERNYEAR; - tdays -= leapdays; - y = newy; - } - { - register long seconds; - - seconds = tdays * SECSPERDAY + 0.5; - tdays = seconds / SECSPERDAY; - rem += seconds - tdays * SECSPERDAY; - } - /* - ** Given the range, we can now fearlessly cast... - */ - idays = tdays; - rem += offset - corr; - while (rem < 0) { - rem += SECSPERDAY; - --idays; - } - while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++idays; - } - while (idays < 0) { - if (increment_overflow(&y, -1)) - return NULL; - idays += year_lengths[isleap(y)]; - } - while (idays >= year_lengths[isleap(y)]) { - idays -= year_lengths[isleap(y)]; - if (increment_overflow(&y, 1)) - return NULL; - } - tmp->tm_year = y; - if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) - return NULL; - tmp->tm_yday = idays; - /* - ** The "extra" mods below avoid overflow problems. - */ - tmp->tm_wday = EPOCH_WDAY + - ((y - EPOCH_YEAR) % DAYSPERWEEK) * - (DAYSPERNYEAR % DAYSPERWEEK) + - leaps_thru_end_of(y - 1) - - leaps_thru_end_of(EPOCH_YEAR - 1) + - idays; - tmp->tm_wday %= DAYSPERWEEK; - if (tmp->tm_wday < 0) - tmp->tm_wday += DAYSPERWEEK; - tmp->tm_hour = (int) (rem / SECSPERHOUR); - rem %= SECSPERHOUR; - tmp->tm_min = (int) (rem / SECSPERMIN); - /* - ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. - */ - tmp->tm_sec = (int) (rem % SECSPERMIN) + hit; - ip = mon_lengths[isleap(y)]; - for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) - idays -= ip[tmp->tm_mon]; - tmp->tm_mday = (int) (idays + 1); - tmp->tm_isdst = 0; -#ifdef TM_GMTOFF - tmp->TM_GMTOFF = offset; -#endif /* defined TM_GMTOFF */ - return tmp; -} - -// ============================================================================ -#if 0 -char * -ctime(timep) -const time_t * const timep; -{ -/* -** Section 4.12.3.2 of X3.159-1989 requires that -** The ctime function converts the calendar time pointed to by timer -** to local time in the form of a string. It is equivalent to -** asctime(localtime(timer)) -*/ - return asctime(localtime(timep)); -} -#endif - -// ============================================================================ -#if 0 -char * -ctime_r(timep, buf) -const time_t * const timep; -char * buf; -{ - struct tm mytm; - - return asctime_r(localtime_r(timep, &mytm), buf); -} -#endif - -/* -** Adapted from code provided by Robert Elz, who writes: -** The "best" way to do mktime I think is based on an idea of Bob -** Kridle's (so its said...) from a long time ago. -** It does a binary search of the time_t space. Since time_t's are -** just 32 bits, its a max of 32 iterations (even at 64 bits it -** would still be very reasonable). -*/ - -#ifndef WRONG -#define WRONG (-1) -#endif /* !defined WRONG */ - -/* -** Simplified normalize logic courtesy Paul Eggert. -*/ - -static int -increment_overflow(number, delta) -int * number; -int delta; -{ - unsigned number0 = (unsigned)*number; - unsigned number1 = (unsigned)(number0 + delta); - - *number = (int)number1; - - if (delta >= 0) { - return ((int)number1 < (int)number0); - } else { - return ((int)number1 > (int)number0); - } -} - -static int -long_increment_overflow(number, delta) -long * number; -int delta; -{ - unsigned long number0 = (unsigned long)*number; - unsigned long number1 = (unsigned long)(number0 + delta); - - *number = (long)number1; - - if (delta >= 0) { - return ((long)number1 < (long)number0); - } else { - return ((long)number1 > (long)number0); - } -} - -static int -normalize_overflow(tensptr, unitsptr, base) -int * const tensptr; -int * const unitsptr; -const int base; -{ - register int tensdelta; - - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow(tensptr, tensdelta); -} - -static int -long_normalize_overflow(tensptr, unitsptr, base) -long * const tensptr; -int * const unitsptr; -const int base; -{ - register int tensdelta; - - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return long_increment_overflow(tensptr, tensdelta); -} - -static int -tmcomp(atmp, btmp) -register const struct tm * const atmp; -register const struct tm * const btmp; -{ - register int result; - - if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && - (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && - (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && - (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && - (result = (atmp->tm_min - btmp->tm_min)) == 0) - result = atmp->tm_sec - btmp->tm_sec; - return result; -} - -static time_t -time2sub(tmp, funcp, offset, okayp, do_norm_secs, sp) -struct tm * const tmp; -struct tm * (* const funcp) P((const time_t*, long, struct tm*,const struct state *sp)); -const long offset; -int * const okayp; -const int do_norm_secs; -const struct state * sp; -{ - register int dir; - register int i, j; - register int saved_seconds; - register long li; - register time_t lo; - register time_t hi; - long y; - time_t newt; - time_t t; - struct tm yourtm, mytm; - - *okayp = FALSE; - yourtm = *tmp; - if (do_norm_secs) { - if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, - SECSPERMIN)) - return WRONG; - } - if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) - return WRONG; - if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) - return WRONG; - y = yourtm.tm_year; - if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR)) - return WRONG; - /* - ** Turn y into an actual year number for now. - ** It is converted back to an offset from TM_YEAR_BASE later. - */ - if (long_increment_overflow(&y, TM_YEAR_BASE)) - return WRONG; - while (yourtm.tm_mday <= 0) { - if (long_increment_overflow(&y, -1)) - return WRONG; - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(li)]; - } - while (yourtm.tm_mday > DAYSPERLYEAR) { - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(li)]; - if (long_increment_overflow(&y, 1)) - return WRONG; - } - for ( ; ; ) { - i = mon_lengths[isleap(y)][yourtm.tm_mon]; - if (yourtm.tm_mday <= i) - break; - yourtm.tm_mday -= i; - if (++yourtm.tm_mon >= MONSPERYEAR) { - yourtm.tm_mon = 0; - if (long_increment_overflow(&y, 1)) - return WRONG; - } - } - if (long_increment_overflow(&y, -TM_YEAR_BASE)) - return WRONG; - yourtm.tm_year = y; - if (yourtm.tm_year != y) - return WRONG; - if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) - saved_seconds = 0; - else if (y + TM_YEAR_BASE < EPOCH_YEAR) { - /* - ** We can't set tm_sec to 0, because that might push the - ** time below the minimum representable time. - ** Set tm_sec to 59 instead. - ** This assumes that the minimum representable time is - ** not in the same minute that a leap second was deleted from, - ** which is a safer assumption than using 58 would be. - */ - if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = SECSPERMIN - 1; - } else { - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; - } - /* - ** Do a binary search (this works whatever time_t's type is). - */ - if (!TYPE_SIGNED(time_t)) { - lo = 0; - hi = lo - 1; - } else if (!TYPE_INTEGRAL(time_t)) { - if (sizeof(time_t) > sizeof(float)) - hi = (time_t) DBL_MAX; - else hi = (time_t) FLT_MAX; - lo = -hi; - } else { - lo = 1; - for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i) - lo *= 2; - hi = -(lo + 1); - } - for ( ; ; ) { - t = lo / 2 + hi / 2; - if (t < lo) - t = lo; - else if (t > hi) - t = hi; - if ((*funcp)(&t, offset, &mytm, sp) == NULL) { - /* - ** Assume that t is too extreme to be represented in - ** a struct tm; arrange things so that it is less - ** extreme on the next pass. - */ - dir = (t > 0) ? 1 : -1; - } else dir = tmcomp(&mytm, &yourtm); - if (dir != 0) { - if (t == lo) { - if (t == TIME_T_MAX) - return WRONG; - ++t; - ++lo; - } else if (t == hi) { - if (t == TIME_T_MIN) - return WRONG; - --t; - --hi; - } - if (lo > hi) - return WRONG; - if (dir > 0) - hi = t; - else lo = t; - continue; - } - if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) - break; - /* - ** Right time, wrong type. - ** Hunt for right time, right type. - ** It's okay to guess wrong since the guess - ** gets checked. - */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ -#ifdef ALL_STATE - if (sp == NULL) - return WRONG; -#endif /* defined ALL_STATE */ - for (i = sp->typecnt - 1; i >= 0; --i) { - if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) - continue; - for (j = sp->typecnt - 1; j >= 0; --j) { - if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) - continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - if ((*funcp)(&newt, offset, &mytm, sp) == NULL) - continue; - if (tmcomp(&mytm, &yourtm) != 0) - continue; - if (mytm.tm_isdst != yourtm.tm_isdst) - continue; - /* - ** We have a match. - */ - t = newt; - goto label; - } - } - return WRONG; - } -label: - newt = t + saved_seconds; - if ((newt < t) != (saved_seconds < 0)) - return WRONG; - t = newt; - if ((*funcp)(&t, offset, tmp, sp)) - *okayp = TRUE; - return t; -} - -static time_t -time2(tmp, funcp, offset, okayp, sp) -struct tm * const tmp; -struct tm * (* const funcp) P((const time_t*, long, struct tm*, - const struct state* sp)); -const long offset; -int * const okayp; -const struct state * sp; -{ - time_t t; - - /* - ** First try without normalization of seconds - ** (in case tm_sec contains a value associated with a leap second). - ** If that fails, try with normalization of seconds. - */ - t = time2sub(tmp, funcp, offset, okayp, FALSE, sp); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp); -} - -static time_t -time1(tmp, funcp, offset, sp) -struct tm * const tmp; -struct tm * (* const funcp) P((const time_t *, long, struct tm *, const struct state* sp)); -const long offset; -const struct state * sp; -{ - register time_t t; - register int samei, otheri; - register int sameind, otherind; - register int i; - register int nseen; - int seen[TZ_MAX_TYPES]; - int types[TZ_MAX_TYPES]; - int okay; - - if (tmp->tm_isdst > 1) - tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay, sp); -#define PCTS 1 -#ifdef PCTS - /* - ** PCTS code courtesy Grant Sullivan. - */ - if (okay) - return t; - if (tmp->tm_isdst < 0) - tmp->tm_isdst = 0; /* reset to std and try again */ -#endif /* defined PCTS */ -#ifndef PCTS - if (okay || tmp->tm_isdst < 0) - return t; -#endif /* !defined PCTS */ - /* - ** We're supposed to assume that somebody took a time of one type - ** and did some math on it that yielded a "struct tm" that's bad. - ** We try to divine the type they started from and adjust to the - ** type they need. - */ - /* - ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ -#ifdef ALL_STATE - if (sp == NULL) - return WRONG; -#endif /* defined ALL_STATE */ - for (i = 0; i < sp->typecnt; ++i) - seen[i] = FALSE; - nseen = 0; - for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) { - seen[sp->types[i]] = TRUE; - types[nseen++] = sp->types[i]; - } - for (sameind = 0; sameind < nseen; ++sameind) { - samei = types[sameind]; - if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) - continue; - for (otherind = 0; otherind < nseen; ++otherind) { - otheri = types[otherind]; - if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay, sp); - if (okay) - return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - } - } - return WRONG; -} - -// ============================================================================ -time_t -mktime_tz(struct tm * const tmp, char const * tz) -{ - struct state st; - if (tzload(tz, &st, TRUE) != 0) { - // not sure what's best here, but for now, we fall back to gmt - gmtload(&st); - } - return time1(tmp, localsub, 0L, &st); -} 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..020c35b --- /dev/null +++ b/libion/ion.c @@ -0,0 +1,156 @@ +/* + * 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 heap_mask, + unsigned int flags, struct ion_handle **handle) +{ + int ret; + struct ion_allocation_data data = { + .len = len, + .align = align, + .heap_mask = heap_mask, + .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_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, + unsigned int flags, int *handle_fd) { + struct ion_handle *handle; + int ret; + + ret = ion_alloc(fd, len, align, heap_mask, flags, &handle); + if (ret < 0) + return ret; + ret = ion_share(fd, handle, handle_fd); + ion_free(fd, handle); + 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; +} + +int ion_sync_fd(int fd, int handle_fd) +{ + struct ion_fd_data data = { + .fd = handle_fd, + }; + return ion_ioctl(fd, ION_IOC_SYNC, &data); +} diff --git a/libion/ion_test.c b/libion/ion_test.c new file mode 100644 index 0000000..0caaa2a --- /dev/null +++ b/libion/ion_test.c @@ -0,0 +1,278 @@ +#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 heap_mask = 1; +int test = -1; +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, heap_mask, 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'}, + {"heap_mask", required_argument, 0, 'h'}, + {"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'}, + }; + int i = 0; + c = getopt_long(argc, argv, "af:h:l:mr:st", 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 'h': + heap_mask = atol(optarg); + break; + case 'a': + test = ALLOC_TEST; + break; + case 'm': + test = MAP_TEST; + break; + case 's': + test = SHARE_TEST; + break; + } + } + printf("test %d, len %u, align %u, map_flags %d, prot %d, heap_mask %d," + " alloc_flags %d\n", test, len, align, map_flags, prot, + heap_mask, 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/logd_write.c b/liblog/logd_write.c index a0a753b..3613d25 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -24,6 +24,8 @@ #include <string.h> #include <stdlib.h> #include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> #include <cutils/logger.h> #include <cutils/logd.h> @@ -37,7 +39,7 @@ #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) #define log_close(filedes) fakeLogClose(filedes) #else -#define log_open(pathname, flags) open(pathname, flags) +#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC) #define log_writev(filedes, vector, count) writev(filedes, vector, count) #define log_close(filedes) close(filedes) #endif @@ -134,6 +136,7 @@ int __android_log_write(int prio, const char *tag, const char *msg) { struct iovec vec[3]; log_id_t log_id = LOG_ID_MAIN; + char tmp_tag[32]; if (!tag) tag = ""; @@ -141,13 +144,18 @@ int __android_log_write(int prio, const char *tag, const char *msg) /* XXX: This needs to go! */ if (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || - !strcmp(tag, "SMS")) + !strcmp(tag, "SMS")) { log_id = LOG_ID_RADIO; + // Inform third party apps/ril/radio.. to use Rlog or RLOG + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; @@ -162,20 +170,27 @@ int __android_log_write(int prio, const char *tag, const char *msg) int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) { struct iovec vec[3]; + char tmp_tag[32]; if (!tag) tag = ""; /* XXX: This needs to go! */ - if (!strcmp(tag, "HTC_RIL") || + if ((bufID != LOG_ID_RADIO) && + (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || - !strcmp(tag, "SMS")) + !strcmp(tag, "SMS"))) { bufID = LOG_ID_RADIO; + // Inform third party apps/ril/radio.. to use Rlog or RLOG + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; 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/libmincrypt/Android.mk b/libmincrypt/Android.mk index b09a941..f58eab9 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -4,13 +4,13 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c index d7124fb..b4ee6af 100644 --- a/libmincrypt/rsa.c +++ b/libmincrypt/rsa.c @@ -1,6 +1,6 @@ /* rsa.c ** -** Copyright 2008, The Android Open Source Project +** Copyright 2012, The Android Open Source Project ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: @@ -13,186 +13,42 @@ ** be used to endorse or promote products derived from this software ** without specific prior written permission. ** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO ** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mincrypt/rsa.h" -#include "mincrypt/sha.h" -/* a[] -= mod */ -static void subM(const RSAPublicKey *key, uint32_t *a) { - int64_t A = 0; - int i; - for (i = 0; i < key->len; ++i) { - A += (uint64_t)a[i] - key->n[i]; - a[i] = (uint32_t)A; - A >>= 32; - } -} - -/* return a[] >= mod */ -static int geM(const RSAPublicKey *key, const uint32_t *a) { - int i; - for (i = key->len; i;) { - --i; - if (a[i] < key->n[i]) return 0; - if (a[i] > key->n[i]) return 1; - } - return 1; /* equal */ -} - -/* montgomery c[] += a * b[] / R % mod */ -static void montMulAdd(const RSAPublicKey *key, - uint32_t* c, - const uint32_t a, - const uint32_t* b) { - uint64_t A = (uint64_t)a * b[0] + c[0]; - uint32_t d0 = (uint32_t)A * key->n0inv; - uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; - int i; - - for (i = 1; i < key->len; ++i) { - A = (A >> 32) + (uint64_t)a * b[i] + c[i]; - B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; - c[i - 1] = (uint32_t)B; - } - - A = (A >> 32) + (B >> 32); - - c[i - 1] = (uint32_t)A; - - if (A >> 32) { - subM(key, c); - } -} - -/* montgomery c[] = a[] * b[] / R % mod */ -static void montMul(const RSAPublicKey *key, - uint32_t* c, - const uint32_t* a, - const uint32_t* b) { - int i; - for (i = 0; i < key->len; ++i) { - c[i] = 0; - } - for (i = 0; i < key->len; ++i) { - montMulAdd(key, c, a[i], b); - } -} - -/* In-place public exponentiation. -** Input and output big-endian byte array in inout. -*/ -static void modpow3(const RSAPublicKey *key, - uint8_t* inout) { - uint32_t a[RSANUMWORDS]; - uint32_t aR[RSANUMWORDS]; - uint32_t aaR[RSANUMWORDS]; - uint32_t *aaa = aR; /* Re-use location. */ - int i; - - /* Convert from big endian byte array to little endian word array. */ - for (i = 0; i < key->len; ++i) { - uint32_t tmp = - (inout[((key->len - 1 - i) * 4) + 0] << 24) | - (inout[((key->len - 1 - i) * 4) + 1] << 16) | - (inout[((key->len - 1 - i) * 4) + 2] << 8) | - (inout[((key->len - 1 - i) * 4) + 3] << 0); - a[i] = tmp; - } +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha); - montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ - montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ - montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha); - /* Make sure aaa < mod; aaa is at most 1x mod too large. */ - if (geM(key, aaa)) { - subM(key, aaa); - } - - /* Convert to bigendian byte array */ - for (i = key->len - 1; i >= 0; --i) { - uint32_t tmp = aaa[i]; - *inout++ = tmp >> 24; - *inout++ = tmp >> 16; - *inout++ = tmp >> 8; - *inout++ = tmp >> 0; - } -} - -/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. -** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the -** other flavor which omits the optional parameter entirely). This code does not -** accept signatures without the optional parameter. -*/ -static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { - 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, - 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, - 0x04,0x14 -}; - -/* Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash. -** Returns 0 on failure, 1 on success. -*/ int RSA_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, const uint8_t *sha) { - uint8_t buf[RSANUMBYTES]; - int i; - - if (key->len != RSANUMWORDS) { - return 0; /* Wrong key passed in. */ - } - - if (len != sizeof(buf)) { - return 0; /* Wrong input length. */ - } - - for (i = 0; i < len; ++i) { - buf[i] = signature[i]; - } - - modpow3(key, buf); - - /* Check pkcs1.5 padding bytes. */ - for (i = 0; i < (int) sizeof(padding); ++i) { - if (buf[i] != padding[i]) { + switch (key->exponent) { + case 3: + return RSA_e_3_verify(key, signature, len, sha); + break; + case 65537: + return RSA_e_f4_verify(key, signature, len, sha); + break; + default: return 0; - } } - - /* Check sha digest matches. */ - for (; i < len; ++i) { - if (buf[i] != *sha++) { - return 0; - } - } - - return 1; } diff --git a/libmincrypt/rsa_e_3.c b/libmincrypt/rsa_e_3.c new file mode 100644 index 0000000..c8c02c4 --- /dev/null +++ b/libmincrypt/rsa_e_3.c @@ -0,0 +1,202 @@ +/* rsa_e_3.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +/* a[] -= mod */ +static void subM(const RSAPublicKey *key, uint32_t *a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +/* return a[] >= mod */ +static int geM(const RSAPublicKey *key, const uint32_t *a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; /* equal */ +} + +/* montgomery c[] += a * b[] / R % mod */ +static void montMulAdd(const RSAPublicKey *key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +/* montgomery c[] = a[] * b[] / R % mod */ +static void montMul(const RSAPublicKey *key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +/* In-place public exponentiation. +** Input and output big-endian byte array in inout. +*/ +static void modpow3(const RSAPublicKey *key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t *aaa = aR; /* Re-use location. */ + int i; + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + + /* Make sure aaa < mod; aaa is at most 1x mod too large. */ + if (geM(key, aaa)) { + subM(key, aaa); + } + + /* Convert to bigendian byte array */ + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +** other flavor which omits the optional parameter entirely). This code does not +** accept signatures without the optional parameter. +*/ +static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, + 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, + 0x04,0x14 +}; + +/* Verify a 2048 bit RSA e=3 PKCS1.5 signature against an expected SHA-1 hash. +** Returns 0 on failure, 1 on success. +*/ +int RSA_e_3_verify(const RSAPublicKey *key, + const uint8_t *signature, + const int len, + const uint8_t *sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; /* Wrong key passed in. */ + } + + if (len != sizeof(buf)) { + return 0; /* Wrong input length. */ + } + + if (key->exponent != 3) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { + buf[i] = signature[i]; + } + + modpow3(key, buf); + + /* Check pkcs1.5 padding bytes. */ + for (i = 0; i < (int) sizeof(padding); ++i) { + if (buf[i] != padding[i]) { + return 0; + } + } + + /* Check sha digest matches. */ + for (; i < len; ++i) { + if (buf[i] != *sha++) { + return 0; + } + } + + return 1; +} diff --git a/libmincrypt/rsa_e_f4.c b/libmincrypt/rsa_e_f4.c new file mode 100644 index 0000000..6701bcc --- /dev/null +++ b/libmincrypt/rsa_e_f4.c @@ -0,0 +1,196 @@ +/* rsa_e_f4.c +** +** Copyright 2012, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +// a[] -= mod +static void subM(const RSAPublicKey* key, + uint32_t* a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} + +// return a[] >= mod +static int geM(const RSAPublicKey* key, + const uint32_t* a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; // equal +} + +// montgomery c[] += a * b[] / R % mod +static void montMulAdd(const RSAPublicKey* key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +// montgomery c[] = a[] * b[] / R % mod +static void montMul(const RSAPublicKey* key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +// In-place public exponentiation. +// Input and output big-endian byte array in inout. +static void modpowF4(const RSAPublicKey* key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t* aaa = aaR; // Re-use location. + int i; + + // Convert from big endian byte array to little endian word array. + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + montMul(key, aR, a, key->rr); // aR = a * RR / R mod M + for (i = 0; i < 16; i += 2) { + montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M + montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M + } + montMul(key, aaa, aR, a); // aaa = aR * a / R mod M + + // Make sure aaa < mod; aaa is at most 1x mod too large. + if (geM(key, aaa)) { + subM(key, aaa); + } + + // Convert to bigendian byte array + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +// other flavor which omits the optional parameter entirely). This code does not +// accept signatures without the optional parameter. +/* +static const uint8_t padding[RSANUMBYTES] = { +0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; +*/ + +// SHA-1 of PKCS1.5 signature padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { + 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, 0x6e, 0xfc, + 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, 0x7c, 0xfb, 0xf1, 0x67 +}; + +// Verify a 2048 bit RSA e=65537 PKCS1.5 signature against an expected +// SHA-1 hash. Returns 0 on failure, 1 on success. +int RSA_e_f4_verify(const RSAPublicKey* key, + const uint8_t* signature, + const int len, + const uint8_t* sha) { + uint8_t buf[RSANUMBYTES]; + int i; + + if (key->len != RSANUMWORDS) { + return 0; // Wrong key passed in. + } + + if (len != sizeof(buf)) { + return 0; // Wrong input length. + } + + if (key->exponent != 65537) { + return 0; // Wrong exponent. + } + + for (i = 0; i < len; ++i) { // Copy input to local workspace. + buf[i] = signature[i]; + } + + modpowF4(key, buf); // In-place exponentiation. + + // Xor sha portion, so it all becomes 00 iff equal. + for (i = len - SHA_DIGEST_SIZE; i < len; ++i) { + buf[i] ^= *sha++; + } + + // Hash resulting buf, in-place. + SHA(buf, len, buf); + + // Compare against expected hash value. + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + if (buf[i] != kExpectedPadShaRsa2048[i]) { + return 0; + } + } + + return 1; // All checked out OK. +} diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index d2935e0..12b4f56 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -24,7 +24,6 @@ import java.security.KeyStore; import java.security.Key; import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; -import sun.misc.BASE64Encoder; /** * Command line tool to extract RSA public keys from X.509 certificates @@ -34,27 +33,42 @@ import sun.misc.BASE64Encoder; class DumpPublicKey { /** * @param key to perform sanity checks on + * @return version number of key. Supported versions are: + * 1: 2048-bit key with e=3 + * 2: 2048-bit key with e=65537 * @throws Exception if the key has the wrong size or public exponent + */ - static void check(RSAPublicKey key) throws Exception { + static int check(RSAPublicKey key) throws Exception { BigInteger pubexp = key.getPublicExponent(); BigInteger modulus = key.getModulus(); + int version; + + if (pubexp.equals(BigInteger.valueOf(3))) { + version = 1; + } else if (pubexp.equals(BigInteger.valueOf(65537))) { + version = 2; + } else { + throw new Exception("Public exponent should be 3 or 65537 but is " + + pubexp.toString(10) + "."); + } - if (!pubexp.equals(BigInteger.valueOf(3))) - throw new Exception("Public exponent should be 3 but is " + - pubexp.toString(10) + "."); - - if (modulus.bitLength() != 2048) + if (modulus.bitLength() != 2048) { throw new Exception("Modulus should be 2048 bits long but is " + modulus.bitLength() + " bits."); + } + + return version; } /** * @param key to output - * @return a C initializer representing this public key. + * @return a String representing this public key. If the key is a + * version 1 key, the string will be a C initializer; this is + * not true for newer key versions. */ static String print(RSAPublicKey key) throws Exception { - check(key); + int version = check(key); BigInteger N = key.getModulus(); @@ -62,6 +76,12 @@ class DumpPublicKey { int nwords = N.bitLength() / 32; // # of 32 bit integers in modulus + if (version > 1) { + result.append("v"); + result.append(Integer.toString(version)); + result.append(" "); + } + result.append("{"); result.append(nwords); diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index 1e08eb6..b940453 100755..100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -29,12 +29,33 @@ static const char DAEMON_NAME[] = "dhcpcd"; static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd"; static const char HOSTNAME_PROP_NAME[] = "net.hostname"; static const char DHCP_PROP_NAME_PREFIX[] = "dhcp"; +static const char DHCP_CONFIG_PATH[] = "/system/etc/dhcpcd/dhcpcd.conf"; 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 +91,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]; - snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, interface); + get_p2p_interface_replacement(interface, p2p_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 +116,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 +138,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 +164,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 -f dhcpcd.conf */ int dhcp_do_request(const char *interface, char *ipaddr, @@ -153,35 +180,38 @@ 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]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; - char daemon_cmd[PROPERTY_VALUE_MAX * 2]; + char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)]; 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, - prop_value, interface); + snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME, + p2p_interface, DHCP_CONFIG_PATH, 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:-f %s %s", DAEMON_NAME, + p2p_interface, DHCP_CONFIG_PATH, 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 +232,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 +261,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 +294,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 +317,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,8 +367,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); - return 0; + return fill_ip_info(interface, ipaddr, gateway, prefixLength, + dns1, dns2, server, lease, vendorInfo); } else { snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value); return -1; diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c index b38e258..34500e7 100644 --- a/libnetutils/dhcpclient.c +++ b/libnetutils/dhcpclient.c @@ -197,7 +197,11 @@ int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info) } switch(opt) { case OPT_SUBNET_MASK: - if (optlen >= 4) info->prefixLength = ipv4NetmaskToPrefixLength(*((uint32_t*)x)); + if (optlen >= 4) { + in_addr_t mask; + memcpy(&mask, x, 4); + info->prefixLength = ipv4NetmaskToPrefixLength(mask); + } break; case OPT_GATEWAY: if (optlen >= 4) memcpy(&info->gateway, x, 4); diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index 186b98c..eb33d06 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -75,9 +75,8 @@ in_addr_t prefixLengthToIpv4Netmask(int prefix_length) int ipv4NetmaskToPrefixLength(in_addr_t mask) { - mask = ntohl(mask); int prefixLength = 0; - uint32_t m = (uint32_t)mask; + uint32_t m = (uint32_t)ntohl(mask); while (m & 0x80000000) { prefixLength++; m = m << 1; @@ -486,7 +485,7 @@ int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) { *prefixLength = 0; } else { - *prefixLength = ipv4NetmaskToPrefixLength((int) + *prefixLength = ipv4NetmaskToPrefixLength( ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr); } } @@ -600,10 +599,6 @@ int ifc_disable(const char *ifname) return result; } -#define RESET_IPV4_ADDRESSES 0x01 -#define RESET_IPV6_ADDRESSES 0x02 -#define RESET_ALL_ADDRESSES (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES) - int ifc_reset_connections(const char *ifname, const int reset_mask) { #ifdef HAVE_ANDROID_OS diff --git a/libnetutils/packet.c b/libnetutils/packet.c index 3ec83fe..be4e0db 100644 --- a/libnetutils/packet.c +++ b/libnetutils/packet.c @@ -39,7 +39,7 @@ int fatal(); -int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index) +int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index) { int s, flag; struct sockaddr_ll bindaddr; diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk index 1745f5a..deac9de 100644 --- a/libnl_2/Android.mk +++ b/libnl_2/Android.mk @@ -1,5 +1,11 @@ +####################################### +# * Netlink cache not implemented +# * Library is not thread safe +####################################### + LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -22,9 +28,10 @@ LOCAL_MODULE := libnl_2 LOCAL_MODULE_TAGS := optional include $(BUILD_STATIC_LIBRARY) -####################################### -# Shared library currently unavailiable -# * Netlink cache not implemented -# * Library is not thread safe -####################################### - +include $(CLEAR_VARS) +LOCAL_SRC_FILES := +LOCAL_WHOLE_STATIC_LIBRARIES:= libnl_2 +LOCAL_SHARED_LIBRARIES:= liblog +LOCAL_MODULE := libnl_2 +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) diff --git a/libnl_2/attr.c b/libnl_2/attr.c index f3a2b58..2ef7590 100644 --- a/libnl_2/attr.c +++ b/libnl_2/attr.c @@ -160,9 +160,31 @@ int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) } return -EINVAL; +} + +/* Add 8 bit integer attribute to netlink message. */ +int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value) +{ + return nla_put(msg, attrtype, sizeof(uint8_t), &value); +} + +/* Add 16 bit integer attribute to netlink message. */ +int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value) +{ + return nla_put(msg, attrtype, sizeof(uint16_t), &value); +} +/* Add 32 bit integer attribute to netlink message. */ +int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value) +{ + return nla_put(msg, attrtype, sizeof(uint32_t), &value); } +/* Add 64 bit integer attribute to netlink message. */ +int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value) +{ + return nla_put(msg, attrtype, sizeof(uint64_t), &value); +} /* Add nested attributes to netlink message. */ /* Takes the attributes found in the nested message and appends them diff --git a/libnl_2/genl/genl.c b/libnl_2/genl/genl.c index 2442993..1a39c6a 100644 --- a/libnl_2/genl/genl.c +++ b/libnl_2/genl/genl.c @@ -20,7 +20,10 @@ #include <unistd.h> #include <stdio.h> #include <sys/time.h> +#include <sys/socket.h> #include <linux/netlink.h> +#include <netlink/genl/ctrl.h> +#include <netlink/genl/family.h> #include "netlink-types.h" /* Get head of attribute data. */ @@ -250,7 +253,6 @@ error: struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ const char *name) { - /* TODO: When will we release this memory ? */ struct genl_family *gf = (struct genl_family *) \ malloc(sizeof(struct genl_family)); if (!gf) @@ -262,7 +264,7 @@ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \ /* Overriding cache pointer as family id for now */ gf->gf_id = (uint16_t) ((uint32_t) cache); - strcpy(gf->gf_name, "nl80211"); + strncpy(gf->gf_name, name, GENL_NAMSIZ); return gf; fail: @@ -272,15 +274,29 @@ fail: int genl_ctrl_resolve(struct nl_sock *sk, const char *name) { + struct nl_cache *cache = NULL; + struct genl_family *gf = NULL; + int id = -1; + /* Hack to support wpa_supplicant */ if (strcmp(name, "nlctrl") == 0) return NETLINK_GENERIC; - else { - int errsv = errno; - fprintf(stderr, \ - "Only nlctrl supported by genl_ctrl_resolve!\n"); - return -errsv; + + if (strcmp(name, "nl80211") != 0) { + fprintf(stderr, "%s is not supported\n", name); + return id; } -} + if (!genl_ctrl_alloc_cache(sk, &cache)) { + gf = genl_ctrl_search_by_name(cache, name); + if (gf) + id = genl_family_get_id(gf); + } + + if (gf) + genl_family_put(gf); + if (cache) + nl_cache_free(cache); + return id; +} diff --git a/libnl_2/msg.c b/libnl_2/msg.c index 283da6e..1303e8a 100644 --- a/libnl_2/msg.c +++ b/libnl_2/msg.c @@ -18,6 +18,7 @@ #include <malloc.h> #include <unistd.h> +#include <sys/socket.h> #include <linux/netlink.h> #include "netlink-types.h" diff --git a/libnl_2/socket.c b/libnl_2/socket.c index d906cac..e94eb9e 100644 --- a/libnl_2/socket.c +++ b/libnl_2/socket.c @@ -127,3 +127,15 @@ int nl_socket_get_fd(struct nl_sock *sk) { return sk->s_fd; } + +void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb) +{ + nl_cb_put(sk->s_cb); + sk->s_cb = cb; + nl_cb_get(cb); +} + +struct nl_cb *nl_socket_get_cb(struct nl_sock *sk) +{ + return nl_cb_get(sk->s_cb); +} diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index ed2ab5e..488003f 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -43,6 +43,13 @@ ifeq ($(TARGET_ARCH),arm) PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer endif +ifeq ($(TARGET_ARCH),mips) +PIXELFLINGER_SRC_FILES += codeflinger/MIPSAssembler.cpp +PIXELFLINGER_SRC_FILES += codeflinger/mips_disassem.c +PIXELFLINGER_SRC_FILES += arch-mips/t32cb16blend.S +PIXELFLINGER_CFLAGS += -fstrict-aliasing -fomit-frame-pointer +endif + LOCAL_SHARED_LIBRARIES := libcutils ifneq ($(TARGET_ARCH),arm) diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S new file mode 100644 index 0000000..c911fbb --- /dev/null +++ b/libpixelflinger/arch-mips/t32cb16blend.S @@ -0,0 +1,264 @@ +/* libs/pixelflinger/t32cb16blend.S +** +** Copyright 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. +*/ + +#ifdef DEBUG +#define DBG +#else +#define DBG # +#endif + +/* + * blend one of 2 16bpp RGB pixels held in dreg selected by shift + * with the 32bpp ABGR pixel held in src and store the result in fb + * + * Assumes that the dreg data is little endian and that + * the the second pixel (shift==16) will be merged into + * the fb result + * + * Uses $t0,$t6,$t7,$t8 + */ + +#if __mips==32 && __mips_isa_rev>=2 + .macro pixel dreg src fb shift + /* + * sA = s >> 24 + * f = 0x100 - (sA + (sA>>7)) + */ +DBG .set noat +DBG rdhwr $at,$2 +DBG .set at + + srl $t7,\src,24 + srl $t6,$t7,7 + addu $t7,$t6 + li $t6,0x100 + subu $t7,$t6,$t7 + + /* red */ + ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11] + mul $t6,$t8,$t7 + ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5] + ext $t8,\src,3,5 # src[7..3] + srl $t6,8 + addu $t8,$t6 + ins \fb,$t8,\shift+6+5,5 # dst[\shift:15..11] + + /* green */ + mul $t8,$t0,$t7 + ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0] + ext $t6,\src,2+8,6 # src[15..10] + srl $t8,8 + addu $t8,$t6 + + /* blue */ + mul $t0,$t0,$t7 + ins \fb,$t8,\shift+5,6 # finish green insertion dst[\shift:10..5] + ext $t6,\src,(3+8+8),5 + srl $t8,$t0,8 + addu $t8,$t6 + ins \fb,$t8,\shift,5 + +DBG .set noat +DBG rdhwr $t8,$2 +DBG subu $t8,$at +DBG sltu $at,$t8,$v0 +DBG movn $v0,$t8,$at +DBG sgtu $at,$t8,$v1 +DBG movn $v1,$t8,$at +DBG .set at + .endm + +#else + + .macro pixel dreg src fb shift + /* + * sA = s >> 24 + * f = 0x100 - (sA + (sA>>7)) + */ +DBG .set push +DBG .set noat +DBG .set mips32r2 +DBG rdhwr $at,$2 +DBG .set pop + + srl $t7,\src,24 + srl $t6,$t7,7 + addu $t7,$t6 + li $t6,0x100 + subu $t7,$t6,$t7 + + /* + * red + * dR = (d >> (6 + 5)) & 0x1f; + * dR = (f*dR)>>8 + * sR = (s >> ( 3)) & 0x1f; + * sR += dR + * fb |= sR << 11 + */ + srl $t8,\dreg,\shift+6+5 +.if \shift==0 + and $t8,0x1f +.endif + mul $t8,$t8,$t7 + srl $t6,\src,3 + and $t6,0x1f + srl $t8,8 + addu $t8,$t6 +.if \shift!=0 + sll $t8,\shift+11 + or \fb,$t8 +.else + sll \fb,$t8,11 +.endif + + /* + * green + * dG = (d >> 5) & 0x3f + * dG = (f*dG) >> 8 + * sG = (s >> ( 8+2))&0x3F; + */ + srl $t8,\dreg,\shift+5 + and $t8,0x3f + mul $t8,$t8,$t7 + srl $t6,\src,8+2 + and $t6,0x3f + srl $t8,8 + addu $t8,$t6 + sll $t8,\shift + 5 + or \fb,$t8 + + /* blue */ +.if \shift!=0 + srl $t8,\dreg,\shift + and $t8,0x1f +.else + and $t8,\dreg,0x1f +.endif + mul $t8,$t8,$t7 + srl $t6,\src,(8+8+3) + and $t6,0x1f + srl $t8,8 + addu $t8,$t6 +.if \shift!=0 + sll $t8,\shift +.endif + or \fb,$t8 +DBG .set push +DBG .set noat +DBG .set mips32r2 +DBG rdhwr $t8,$2 +DBG subu $t8,$at +DBG sltu $at,$t8,$v0 +DBG movn $v0,$t8,$at +DBG sgtu $at,$t8,$v1 +DBG movn $v1,$t8,$at +DBG .set pop + .endm +#endif + + .text + .align + + .global scanline_t32cb16blend_mips + .ent scanline_t32cb16blend_mips +scanline_t32cb16blend_mips: +DBG li $v0,0xffffffff +DBG li $v1,0 + /* Align the destination if necessary */ + and $t0,$a0,3 + beqz $t0,aligned + + /* as long as there is at least one pixel */ + beqz $a2,done + + lw $t4,($a1) + addu $a0,2 + addu $a1,4 + beqz $t4,1f + lhu $t3,-2($a0) + pixel $t3,$t4,$t1,0 + sh $t1,-2($a0) +1: subu $a2,1 + +aligned: + /* Check to see if its worth unrolling the loop */ + subu $a2,4 + bltz $a2,tail + + /* Process 4 pixels at a time */ +fourpixels: + /* 1st pair of pixels */ + lw $t4,0($a1) + lw $t5,4($a1) + addu $a0,8 + addu $a1,16 + + /* both are zero, skip this pair */ + or $t3,$t4,$t5 + beqz $t3,1f + + /* load the destination */ + lw $t3,-8($a0) + + pixel $t3,$t4,$t1,0 + pixel $t3,$t5,$t1,16 + sw $t1,-8($a0) + +1: + /* 2nd pair of pixels */ + lw $t4,-8($a1) + lw $t5,-4($a1) + + /* both are zero, skip this pair */ + or $t3,$t4,$t5 + beqz $t3,1f + + /* load the destination */ + lw $t3,-4($a0) + + pixel $t3,$t4,$t1,0 + pixel $t3,$t5,$t1,16 + sw $t1,-4($a0) + +1: subu $a2,4 + bgtz $a2,fourpixels + +tail: + /* the pixel count underran, restore it now */ + addu $a2,4 + + /* handle the last 0..3 pixels */ + beqz $a2,done +onepixel: + lw $t4,($a1) + addu $a0,2 + addu $a1,4 + beqz $t4,1f + lhu $t3,-2($a0) + pixel $t3,$t4,$t1,0 + sh $t1,-2($a0) +1: subu $a2,1 + bnez $a2,onepixel +done: +DBG .set push +DBG .set mips32r2 +DBG rdhwr $a0,$3 +DBG mul $v0,$a0 +DBG mul $v1,$a0 +DBG .set pop + j $ra + .end scanline_t32cb16blend_mips diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp index 0dc5037..c4f42f5 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.cpp +++ b/libpixelflinger/codeflinger/ARMAssembler.cpp @@ -76,6 +76,11 @@ void ARMAssembler::reset() mComments.clear(); } +int ARMAssembler::getCodegenArch() +{ + return CODEGEN_ARCH_ARM; +} + // ---------------------------------------------------------------------------- void ARMAssembler::disassemble(const char* name) @@ -444,5 +449,146 @@ void ARMAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width) *mPC++ = (cc<<28) | 0x7E00000 | ((width-1)<<16) | (Rd<<12) | (lsb<<7) | 0x50 | Rn; } +#if 0 +#pragma mark - +#pragma mark Addressing modes... +#endif + +int ARMAssembler::buildImmediate( + uint32_t immediate, uint32_t& rot, uint32_t& imm) +{ + rot = 0; + imm = immediate; + if (imm > 0x7F) { // skip the easy cases + while (!(imm&3) || (imm&0xFC000000)) { + uint32_t newval; + newval = imm >> 2; + newval |= (imm&3) << 30; + imm = newval; + rot += 2; + if (rot == 32) { + rot = 0; + break; + } + } + } + rot = (16 - (rot>>1)) & 0xF; + + if (imm>=0x100) + return -EINVAL; + + if (((imm>>(rot<<1)) | (imm<<(32-(rot<<1)))) != immediate) + return -1; + + return 0; +} + +// shifters... + +bool ARMAssembler::isValidImmediate(uint32_t immediate) +{ + uint32_t rot, imm; + return buildImmediate(immediate, rot, imm) == 0; +} + +uint32_t ARMAssembler::imm(uint32_t immediate) +{ + uint32_t rot, imm; + int err = buildImmediate(immediate, rot, imm); + + LOG_ALWAYS_FATAL_IF(err==-EINVAL, + "immediate %08x cannot be encoded", + immediate); + + LOG_ALWAYS_FATAL_IF(err, + "immediate (%08x) encoding bogus!", + immediate); + + return (1<<25) | (rot<<8) | imm; +} + +uint32_t ARMAssembler::reg_imm(int Rm, int type, uint32_t shift) +{ + return ((shift&0x1F)<<7) | ((type&0x3)<<5) | (Rm&0xF); +} + +uint32_t ARMAssembler::reg_rrx(int Rm) +{ + return (ROR<<5) | (Rm&0xF); +} + +uint32_t ARMAssembler::reg_reg(int Rm, int type, int Rs) +{ + return ((Rs&0xF)<<8) | ((type&0x3)<<5) | (1<<4) | (Rm&0xF); +} + +// addressing modes... +// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) +uint32_t ARMAssembler::immed12_pre(int32_t immed12, int W) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + return (1<<24) | (((uint32_t(immed12)>>31)^1)<<23) | + ((W&1)<<21) | (abs(immed12)&0x7FF); +} + +uint32_t ARMAssembler::immed12_post(int32_t immed12) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + + return (((uint32_t(immed12)>>31)^1)<<23) | (abs(immed12)&0x7FF); +} + +uint32_t ARMAssembler::reg_scale_pre(int Rm, int type, + uint32_t shift, int W) +{ + return (1<<25) | (1<<24) | + (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | + reg_imm(abs(Rm), type, shift); +} + +uint32_t ARMAssembler::reg_scale_post(int Rm, int type, uint32_t shift) +{ + return (1<<25) | (((uint32_t(Rm)>>31)^1)<<23) | reg_imm(abs(Rm), type, shift); +} + +// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) +uint32_t ARMAssembler::immed8_pre(int32_t immed8, int W) +{ + uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + + return (1<<24) | (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | + ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF)); +} + +uint32_t ARMAssembler::immed8_post(int32_t immed8) +{ + uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + + return (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | + (((offset&0xF0)<<4) | (offset&0xF)); +} + +uint32_t ARMAssembler::reg_pre(int Rm, int W) +{ + return (1<<24) | (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | (abs(Rm)&0xF); +} + +uint32_t ARMAssembler::reg_post(int Rm) +{ + return (((uint32_t(Rm)>>31)^1)<<23) | (abs(Rm)&0xF); +} + }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h index e7f038a..06c66dd 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.h +++ b/libpixelflinger/codeflinger/ARMAssembler.h @@ -52,11 +52,42 @@ public: virtual void reset(); virtual int generate(const char* name); + virtual int getCodegenArch(); virtual void prolog(); virtual void epilog(uint32_t touched); virtual void comment(const char* string); + + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + // shifters... + virtual bool isValidImmediate(uint32_t immed); + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + virtual uint32_t imm(uint32_t immediate); + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift); + virtual uint32_t reg_rrx(int Rm); + virtual uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed12_pre(int32_t immed12, int W=0); + virtual uint32_t immed12_post(int32_t immed12); + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed8_pre(int32_t immed8, int W=0); + virtual uint32_t immed8_post(int32_t immed8); + virtual uint32_t reg_pre(int Rm, int W=0); + virtual uint32_t reg_post(int Rm); + + virtual void dataProcessing(int opcode, int cc, int s, int Rd, int Rn, uint32_t Op2); @@ -83,21 +114,23 @@ public: virtual uint32_t* pcForLabel(const char* label); virtual void LDR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSB(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSH(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void STRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); + + virtual void LDM(int cc, int dir, int Rn, int W, uint32_t reg_list); virtual void STM(int cc, int dir, diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp index 7fa0de0..82180ee 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.cpp @@ -32,77 +32,15 @@ ARMAssemblerInterface::~ARMAssemblerInterface() { } -int ARMAssemblerInterface::buildImmediate( - uint32_t immediate, uint32_t& rot, uint32_t& imm) -{ - rot = 0; - imm = immediate; - if (imm > 0x7F) { // skip the easy cases - while (!(imm&3) || (imm&0xFC000000)) { - uint32_t newval; - newval = imm >> 2; - newval |= (imm&3) << 30; - imm = newval; - rot += 2; - if (rot == 32) { - rot = 0; - break; - } - } - } - rot = (16 - (rot>>1)) & 0xF; - - if (imm>=0x100) - return -EINVAL; - - if (((imm>>(rot<<1)) | (imm<<(32-(rot<<1)))) != immediate) - return -1; - - return 0; -} - -// shifters... - -bool ARMAssemblerInterface::isValidImmediate(uint32_t immediate) -{ - uint32_t rot, imm; - return buildImmediate(immediate, rot, imm) == 0; -} - -uint32_t ARMAssemblerInterface::imm(uint32_t immediate) -{ - uint32_t rot, imm; - int err = buildImmediate(immediate, rot, imm); - - LOG_ALWAYS_FATAL_IF(err==-EINVAL, - "immediate %08x cannot be encoded", - immediate); - - LOG_ALWAYS_FATAL_IF(err, - "immediate (%08x) encoding bogus!", - immediate); +// -------------------------------------------------------------------- - return (1<<25) | (rot<<8) | imm; -} - -uint32_t ARMAssemblerInterface::reg_imm(int Rm, int type, uint32_t shift) -{ - return ((shift&0x1F)<<7) | ((type&0x3)<<5) | (Rm&0xF); -} - -uint32_t ARMAssemblerInterface::reg_rrx(int Rm) -{ - return (ROR<<5) | (Rm&0xF); -} +// The following two functions are static and used for initializers +// in the original ARM code. The above versions (without __), are now +// virtual, and can be overridden in the MIPS code. But since these are +// needed at initialization time, they must be static. Not thrilled with +// this implementation, but it works... -uint32_t ARMAssemblerInterface::reg_reg(int Rm, int type, int Rs) -{ - return ((Rs&0xF)<<8) | ((type&0x3)<<5) | (1<<4) | (Rm&0xF); -} - -// addressing modes... -// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) -uint32_t ARMAssemblerInterface::immed12_pre(int32_t immed12, int W) +uint32_t ARMAssemblerInterface::__immed12_pre(int32_t immed12, int W) { LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, "LDR(B)/STR(B)/PLD immediate too big (%08x)", @@ -111,30 +49,7 @@ uint32_t ARMAssemblerInterface::immed12_pre(int32_t immed12, int W) ((W&1)<<21) | (abs(immed12)&0x7FF); } -uint32_t ARMAssemblerInterface::immed12_post(int32_t immed12) -{ - LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, - "LDR(B)/STR(B)/PLD immediate too big (%08x)", - immed12); - - return (((uint32_t(immed12)>>31)^1)<<23) | (abs(immed12)&0x7FF); -} - -uint32_t ARMAssemblerInterface::reg_scale_pre(int Rm, int type, - uint32_t shift, int W) -{ - return (1<<25) | (1<<24) | - (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | - reg_imm(abs(Rm), type, shift); -} - -uint32_t ARMAssemblerInterface::reg_scale_post(int Rm, int type, uint32_t shift) -{ - return (1<<25) | (((uint32_t(Rm)>>31)^1)<<23) | reg_imm(abs(Rm), type, shift); -} - -// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) -uint32_t ARMAssemblerInterface::immed8_pre(int32_t immed8, int W) +uint32_t ARMAssemblerInterface::__immed8_pre(int32_t immed8, int W) { uint32_t offset = abs(immed8); @@ -146,28 +61,6 @@ uint32_t ARMAssemblerInterface::immed8_pre(int32_t immed8, int W) ((W&1)<<21) | (((offset&0xF0)<<4)|(offset&0xF)); } -uint32_t ARMAssemblerInterface::immed8_post(int32_t immed8) -{ - uint32_t offset = abs(immed8); - - LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, - "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", - immed8); - - return (1<<22) | (((uint32_t(immed8)>>31)^1)<<23) | - (((offset&0xF0)<<4) | (offset&0xF)); -} - -uint32_t ARMAssemblerInterface::reg_pre(int Rm, int W) -{ - return (1<<24) | (((uint32_t(Rm)>>31)^1)<<23) | ((W&1)<<21) | (abs(Rm)&0xF); -} - -uint32_t ARMAssemblerInterface::reg_post(int Rm) -{ - return (((uint32_t(Rm)>>31)^1)<<23) | (abs(Rm)&0xF); -} - }; // namespace android diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h index 796342a..9991980 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h @@ -62,33 +62,40 @@ public: LSAVED = LR4|LR5|LR6|LR7|LR8|LR9|LR10|LR11 | LLR }; + enum { + CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS + }; + // ----------------------------------------------------------------------- // shifters and addressing modes // ----------------------------------------------------------------------- - // shifters... - static bool isValidImmediate(uint32_t immed); - static int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + // these static versions are used for initializers on LDxx/STxx below + static uint32_t __immed12_pre(int32_t immed12, int W=0); + static uint32_t __immed8_pre(int32_t immed12, int W=0); + + virtual bool isValidImmediate(uint32_t immed) = 0; + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm) = 0; - static uint32_t imm(uint32_t immediate); - static uint32_t reg_imm(int Rm, int type, uint32_t shift); - static uint32_t reg_rrx(int Rm); - static uint32_t reg_reg(int Rm, int type, int Rs); + virtual uint32_t imm(uint32_t immediate) = 0; + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift) = 0; + virtual uint32_t reg_rrx(int Rm) = 0; + virtual uint32_t reg_reg(int Rm, int type, int Rs) = 0; // addressing modes... // LDR(B)/STR(B)/PLD // (immediate and Rm can be negative, which indicates U=0) - static uint32_t immed12_pre(int32_t immed12, int W=0); - static uint32_t immed12_post(int32_t immed12); - static uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); - static uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + virtual uint32_t immed12_pre(int32_t immed12, int W=0) = 0; + virtual uint32_t immed12_post(int32_t immed12) = 0; + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0) = 0; + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0) = 0; // LDRH/LDRSB/LDRSH/STRH // (immediate and Rm can be negative, which indicates U=0) - static uint32_t immed8_pre(int32_t immed8, int W=0); - static uint32_t immed8_post(int32_t immed8); - static uint32_t reg_pre(int Rm, int W=0); - static uint32_t reg_post(int Rm); + virtual uint32_t immed8_pre(int32_t immed8, int W=0) = 0; + virtual uint32_t immed8_post(int32_t immed8) = 0; + virtual uint32_t reg_pre(int Rm, int W=0) = 0; + virtual uint32_t reg_post(int Rm) = 0; // ----------------------------------------------------------------------- // basic instructions & code generation @@ -98,6 +105,7 @@ public: virtual void reset() = 0; virtual int generate(const char* name) = 0; virtual void disassemble(const char* name) = 0; + virtual int getCodegenArch() = 0; // construct prolog and epilog virtual void prolog() = 0; @@ -143,22 +151,22 @@ public: // data transfer... virtual void LDR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void LDRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void STR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void STRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)) = 0; + int Rn, uint32_t offset = __immed12_pre(0)) = 0; virtual void LDRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; virtual void LDRSB(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; virtual void LDRSH(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; virtual void STRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)) = 0; + int Rn, uint32_t offset = __immed8_pre(0)) = 0; // block data transfer... virtual void LDM(int cc, int dir, diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp index c57d7da..7feed62 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp @@ -55,6 +55,10 @@ int ARMAssemblerProxy::generate(const char* name) { void ARMAssemblerProxy::disassemble(const char* name) { return mTarget->disassemble(name); } +int ARMAssemblerProxy::getCodegenArch() +{ + return mTarget->getCodegenArch(); +} void ARMAssemblerProxy::prolog() { mTarget->prolog(); } @@ -66,6 +70,93 @@ void ARMAssemblerProxy::comment(const char* string) { } + +// addressing modes + +bool ARMAssemblerProxy::isValidImmediate(uint32_t immed) +{ + return mTarget->isValidImmediate(immed); +} + +int ARMAssemblerProxy::buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm) +{ + return mTarget->buildImmediate(i, rot, imm); +} + + + +uint32_t ARMAssemblerProxy::imm(uint32_t immediate) +{ + return mTarget->imm(immediate); +} + +uint32_t ARMAssemblerProxy::reg_imm(int Rm, int type, uint32_t shift) +{ + return mTarget->reg_imm(Rm, type, shift); +} + +uint32_t ARMAssemblerProxy::reg_rrx(int Rm) +{ + return mTarget->reg_rrx(Rm); +} + +uint32_t ARMAssemblerProxy::reg_reg(int Rm, int type, int Rs) +{ + return mTarget->reg_reg(Rm, type, Rs); +} + + +// addressing modes... +// LDR(B)/STR(B)/PLD +// (immediate and Rm can be negative, which indicates U=0) +uint32_t ARMAssemblerProxy::immed12_pre(int32_t immed12, int W) +{ + return mTarget->immed12_pre(immed12, W); +} + +uint32_t ARMAssemblerProxy::immed12_post(int32_t immed12) +{ + return mTarget->immed12_post(immed12); +} + +uint32_t ARMAssemblerProxy::reg_scale_pre(int Rm, int type, uint32_t shift, int W) +{ + return mTarget->reg_scale_pre(Rm, type, shift, W); +} + +uint32_t ARMAssemblerProxy::reg_scale_post(int Rm, int type, uint32_t shift) +{ + return mTarget->reg_scale_post(Rm, type, shift); +} + + +// LDRH/LDRSB/LDRSH/STRH +// (immediate and Rm can be negative, which indicates U=0) +uint32_t ARMAssemblerProxy::immed8_pre(int32_t immed8, int W) +{ + return mTarget->immed8_pre(immed8, W); +} + +uint32_t ARMAssemblerProxy::immed8_post(int32_t immed8) +{ + return mTarget->immed8_post(immed8); +} + +uint32_t ARMAssemblerProxy::reg_pre(int Rm, int W) +{ + return mTarget->reg_pre(Rm, W); +} + +uint32_t ARMAssemblerProxy::reg_post(int Rm) +{ + return mTarget->reg_post(Rm); +} + + +//------------------------------------------------------------------------ + + + void ARMAssemblerProxy::dataProcessing( int opcode, int cc, int s, int Rd, int Rn, uint32_t Op2) { diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h index 8c7f270..5e3f763 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h +++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h @@ -42,11 +42,40 @@ public: virtual void reset(); virtual int generate(const char* name); virtual void disassemble(const char* name); + virtual int getCodegenArch(); virtual void prolog(); virtual void epilog(uint32_t touched); virtual void comment(const char* string); + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + virtual bool isValidImmediate(uint32_t immed); + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + virtual uint32_t imm(uint32_t immediate); + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift); + virtual uint32_t reg_rrx(int Rm); + virtual uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed12_pre(int32_t immed12, int W=0); + virtual uint32_t immed12_post(int32_t immed12); + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed8_pre(int32_t immed8, int W=0); + virtual uint32_t immed8_post(int32_t immed8); + virtual uint32_t reg_pre(int Rm, int W=0); + virtual uint32_t reg_post(int Rm); + + virtual void dataProcessing(int opcode, int cc, int s, int Rd, int Rn, uint32_t Op2); @@ -73,21 +102,21 @@ public: uint32_t* pcForLabel(const char* label); virtual void LDR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STR (int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void STRB(int cc, int Rd, - int Rn, uint32_t offset = immed12_pre(0)); + int Rn, uint32_t offset = __immed12_pre(0)); virtual void LDRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSB(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDRSH(int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void STRH (int cc, int Rd, - int Rn, uint32_t offset = immed8_pre(0)); + int Rn, uint32_t offset = __immed8_pre(0)); virtual void LDM(int cc, int dir, int Rn, int W, uint32_t reg_list); virtual void STM(int cc, int dir, diff --git a/libpixelflinger/codeflinger/CodeCache.cpp b/libpixelflinger/codeflinger/CodeCache.cpp index a713feb..f9ae00a 100644 --- a/libpixelflinger/codeflinger/CodeCache.cpp +++ b/libpixelflinger/codeflinger/CodeCache.cpp @@ -22,8 +22,11 @@ #include <unistd.h> #include <sys/mman.h> -#include <cutils/log.h> +#include <cutils/ashmem.h> #include <cutils/atomic.h> +#define LOG_TAG "CodeCache" +#include <cutils/log.h> + #include "codeflinger/CodeCache.h" @@ -36,14 +39,75 @@ namespace android { #include <errno.h> #endif +#if defined(__mips__) +#include <asm/cachectl.h> +#include <errno.h> +#endif + +// ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- +// A dlmalloc mspace is used to manage the code cache over a mmaped region. +#define HAVE_MMAP 0 +#define HAVE_MREMAP 0 +#define HAVE_MORECORE 0 +#define MALLOC_ALIGNMENT 16 +#define MSPACES 1 +#define NO_MALLINFO 1 +#define ONLY_MSPACES 1 +// Custom heap error handling. +#define PROCEED_ON_ERROR 0 +static void heap_error(const char* msg, const char* function, void* p); +#define CORRUPTION_ERROR_ACTION(m) \ + heap_error("HEAP MEMORY CORRUPTION", __FUNCTION__, NULL) +#define USAGE_ERROR_ACTION(m,p) \ + heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p) + +#include "../../../../bionic/libc/upstream-dlmalloc/malloc.c" + +static void heap_error(const char* msg, const char* function, void* p) { + ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: CODE FLINGER: %s IN %s addr=%p", + msg, function, p); + /* So that we can get a memory dump around p */ + *((int **) 0xdeadbaad) = (int *) p; +} + +// ---------------------------------------------------------------------------- + +static void* gExecutableStore = NULL; +static mspace gMspace = NULL; +const size_t kMaxCodeCacheCapacity = 1024 * 1024; + +static mspace getMspace() +{ + if (gExecutableStore == NULL) { + int fd = ashmem_create_region("CodeFlinger code cache", + kMaxCodeCacheCapacity); + LOG_ALWAYS_FATAL_IF(fd < 0, + "Creating code cache, ashmem_create_region " + "failed with error '%s'", strerror(errno)); + gExecutableStore = mmap(NULL, kMaxCodeCacheCapacity, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE, fd, 0); + LOG_ALWAYS_FATAL_IF(gExecutableStore == NULL, + "Creating code cache, mmap failed with error " + "'%s'", strerror(errno)); + close(fd); + gMspace = create_mspace_with_base(gExecutableStore, kMaxCodeCacheCapacity, + /*locked=*/ false); + mspace_set_footprint_limit(gMspace, kMaxCodeCacheCapacity); + } + return gMspace; +} + Assembly::Assembly(size_t size) : mCount(1), mSize(0) { mBase = (uint32_t*)mspace_malloc(getMspace(), size); + LOG_ALWAYS_FATAL_IF(mBase == NULL, + "Failed to create Assembly of size %zd in executable " + "store of size %zd", size, kMaxCodeCacheCapacity); mSize = size; - ensureMbaseExecutable(); } Assembly::~Assembly() @@ -77,31 +141,13 @@ uint32_t* Assembly::base() const ssize_t Assembly::resize(size_t newSize) { mBase = (uint32_t*)mspace_realloc(getMspace(), mBase, newSize); + LOG_ALWAYS_FATAL_IF(mBase == NULL, + "Failed to resize Assembly to %zd in code cache " + "of size %zd", newSize, kMaxCodeCacheCapacity); mSize = newSize; - ensureMbaseExecutable(); return size(); } -mspace Assembly::getMspace() -{ - static mspace msp = create_contiguous_mspace(2 * 1024, 1024 * 1024, /*locked=*/ false); - return msp; -} - -void Assembly::ensureMbaseExecutable() -{ - long pagesize = sysconf(_SC_PAGESIZE); - long pagemask = ~(pagesize - 1); // assumes pagesize is a power of 2 - - uint32_t* pageStart = (uint32_t*) (((uintptr_t) mBase) & pagemask); - size_t adjustedLength = (mBase - pageStart) * sizeof(uint32_t) + mSize; - - if (mBase && mprotect(pageStart, adjustedLength, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { - mspace_free(getMspace(), mBase); - mBase = NULL; - } -} - // ---------------------------------------------------------------------------- CodeCache::CodeCache(size_t size) @@ -155,12 +201,12 @@ int CodeCache::cache( const AssemblyKeyBase& keyBase, mCacheInUse += assemblySize; mWhen++; // synchronize caches... -#if defined(__arm__) +#if defined(__arm__) || defined(__mips__) const long base = long(assembly->base()); const long curr = base + long(assembly->size()); err = cacheflush(base, curr, 0); - ALOGE_IF(err, "__ARM_NR_cacheflush error %s\n", - strerror(errno)); + ALOGE_IF(err, "cacheflush error %s\n", + strerror(errno)); #endif } diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h index aaafd26..54fd69b 100644 --- a/libpixelflinger/codeflinger/CodeCache.h +++ b/libpixelflinger/codeflinger/CodeCache.h @@ -22,7 +22,6 @@ #include <stdint.h> #include <pthread.h> #include <sys/types.h> -#include <cutils/mspace.h> #include "tinyutils/KeyedVector.h" #include "tinyutils/smartpointer.h" @@ -68,9 +67,6 @@ public: typedef void weakref_type; private: - static mspace getMspace(); - void ensureMbaseExecutable(); - mutable int32_t mCount; uint32_t* mBase; size_t mSize; diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp index f1d81b2..1ddf93d 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.cpp +++ b/libpixelflinger/codeflinger/GGLAssembler.cpp @@ -31,7 +31,8 @@ namespace android { // ---------------------------------------------------------------------------- GGLAssembler::GGLAssembler(ARMAssemblerInterface* target) - : ARMAssemblerProxy(target), RegisterAllocator(), mOptLevel(7) + : ARMAssemblerProxy(target), + RegisterAllocator(ARMAssemblerProxy::getCodegenArch()), mOptLevel(7) { } @@ -230,7 +231,9 @@ int GGLAssembler::scanline_core(const needs_t& needs, context_t const* c) // texel generation build_textures(parts, regs); - } + if (registerFile().status()) + return registerFile().status(); + } if ((blending & (FACTOR_DST|BLEND_DST)) || (mMasking && !mAllMasked) || @@ -890,6 +893,15 @@ void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits) return; } + if (getCodegenArch() == CODEGEN_ARCH_MIPS) { + // MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr + // the below ' while (mask)' code is buggy on mips + // since mips returns true on isValidImmediate() + // then we get multiple AND instr (positive logic) + AND( AL, 0, d, s, imm(mask) ); + return; + } + int negative_logic = !isValidImmediate(mask); if (negative_logic) { mask = ~mask & size; @@ -1002,6 +1014,15 @@ void GGLAssembler::base_offset( // cheezy register allocator... // ---------------------------------------------------------------------------- +// Modified to support MIPS processors, in a very simple way. We retain the +// (Arm) limit of 16 total registers, but shift the mapping of those registers +// from 0-15, to 2-17. Register 0 on Mips cannot be used as GP registers, and +// register 1 has a traditional use as a temp). + +RegisterAllocator::RegisterAllocator(int arch) : mRegs(arch) +{ +} + void RegisterAllocator::reset() { mRegs.reset(); @@ -1029,16 +1050,22 @@ RegisterAllocator::RegisterFile& RegisterAllocator::registerFile() // ---------------------------------------------------------------------------- -RegisterAllocator::RegisterFile::RegisterFile() - : mRegs(0), mTouched(0), mStatus(0) +RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch) + : mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0) { + if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) { + mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17 + } reserve(ARMAssemblerInterface::SP); reserve(ARMAssemblerInterface::PC); } -RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs) - : mRegs(rhs.mRegs), mTouched(rhs.mTouched) +RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch) + : mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0) { + if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) { + mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17 + } } RegisterAllocator::RegisterFile::~RegisterFile() @@ -1057,8 +1084,12 @@ void RegisterAllocator::RegisterFile::reset() reserve(ARMAssemblerInterface::PC); } +// RegisterFile::reserve() take a register parameter in the +// range 0-15 (Arm compatible), but on a Mips processor, will +// return the actual allocated register in the range 2-17. int RegisterAllocator::RegisterFile::reserve(int reg) { + reg += mRegisterOffset; LOG_ALWAYS_FATAL_IF(isUsed(reg), "reserving register %d, but already in use", reg); @@ -1067,6 +1098,7 @@ int RegisterAllocator::RegisterFile::reserve(int reg) return reg; } +// This interface uses regMask in range 2-17 on MIPS, no translation. void RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask) { mRegs |= regMask; @@ -1075,7 +1107,7 @@ void RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask) int RegisterAllocator::RegisterFile::isUsed(int reg) const { - LOG_ALWAYS_FATAL_IF(reg>=16, "invalid register %d", reg); + LOG_ALWAYS_FATAL_IF(reg>=16+(int)mRegisterOffset, "invalid register %d", reg); return mRegs & (1<<reg); } @@ -1086,10 +1118,10 @@ int RegisterAllocator::RegisterFile::obtain() 6, 7, 8, 9, 10, 11 }; const int nbreg = sizeof(priorityList); - int i, r; + int i, r, reg; for (i=0 ; i<nbreg ; i++) { r = priorityList[i]; - if (!isUsed(r)) { + if (!isUsed(r + mRegisterOffset)) { break; } } @@ -1102,18 +1134,20 @@ int RegisterAllocator::RegisterFile::obtain() // the code will never be run anyway. return ARMAssemblerInterface::SP; } - reserve(r); - return r; + reg = reserve(r); // Param in Arm range 0-15, returns range 2-17 on Mips. + return reg; } bool RegisterAllocator::RegisterFile::hasFreeRegs() const { - return ((mRegs & 0xFFFF) == 0xFFFF) ? false : true; + uint32_t regs = mRegs >> mRegisterOffset; // MIPS fix. + return ((regs & 0xFFFF) == 0xFFFF) ? false : true; } int RegisterAllocator::RegisterFile::countFreeRegs() const { - int f = ~mRegs & 0xFFFF; + uint32_t regs = mRegs >> mRegisterOffset; // MIPS fix. + int f = ~regs & 0xFFFF; // now count number of 1 f = (f & 0x5555) + ((f>>1) & 0x5555); f = (f & 0x3333) + ((f>>2) & 0x3333); @@ -1124,18 +1158,24 @@ int RegisterAllocator::RegisterFile::countFreeRegs() const void RegisterAllocator::RegisterFile::recycle(int reg) { - LOG_FATAL_IF(!isUsed(reg), - "recycling unallocated register %d", - reg); + // commented out, since common failure of running out of regs + // triggers this assertion. Since the code is not execectued + // in that case, it does not matter. No reason to FATAL err. + // LOG_FATAL_IF(!isUsed(reg), + // "recycling unallocated register %d", + // reg); mRegs &= ~(1<<reg); } void RegisterAllocator::RegisterFile::recycleSeveral(uint32_t regMask) { - LOG_FATAL_IF((mRegs & regMask)!=regMask, - "recycling unallocated registers " - "(recycle=%08x, allocated=%08x, unallocated=%08x)", - regMask, mRegs, mRegs®Mask); + // commented out, since common failure of running out of regs + // triggers this assertion. Since the code is not execectued + // in that case, it does not matter. No reason to FATAL err. + // LOG_FATAL_IF((mRegs & regMask)!=regMask, + // "recycling unallocated registers " + // "(recycle=%08x, allocated=%08x, unallocated=%08x)", + // regMask, mRegs, mRegs®Mask); mRegs &= ~regMask; } diff --git a/libpixelflinger/codeflinger/GGLAssembler.h b/libpixelflinger/codeflinger/GGLAssembler.h index d1d29f0..dd5f48e 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.h +++ b/libpixelflinger/codeflinger/GGLAssembler.h @@ -43,6 +43,7 @@ class RegisterAllocator public: class RegisterFile; + RegisterAllocator(int arch); RegisterFile& registerFile(); int reserveReg(int reg); int obtainReg(); @@ -52,8 +53,8 @@ public: class RegisterFile { public: - RegisterFile(); - RegisterFile(const RegisterFile& rhs); + RegisterFile(int arch); + RegisterFile(const RegisterFile& rhs, int arch); ~RegisterFile(); void reset(); @@ -86,6 +87,9 @@ public: uint32_t mRegs; uint32_t mTouched; uint32_t mStatus; + int mArch; + uint32_t mRegisterOffset; // lets reg alloc use 2..17 for mips + // while arm uses 0..15 }; class Scratch diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp new file mode 100644 index 0000000..7888a0e --- /dev/null +++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp @@ -0,0 +1,1957 @@ +/* libs/pixelflinger/codeflinger/MIPSAssembler.cpp +** +** Copyright 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. +*/ + + +/* MIPS assembler and ARM->MIPS assembly translator +** +** The approach is to leave the GGLAssembler and associated files largely +** un-changed, still utilizing all Arm instruction generation. Via the +** ArmToMipsAssembler (subclassed from ArmAssemblerInterface) each Arm +** instruction is translated to one or more Mips instructions as necessary. This +** is clearly less efficient than a direct implementation within the +** GGLAssembler, but is far cleaner, more maintainable, and has yielded very +** significant performance gains on Mips compared to the generic pixel pipeline. +** +** +** GGLAssembler changes +** +** - The register allocator has been modified to re-map Arm registers 0-15 to mips +** registers 2-17. Mips register 0 cannot be used as general-purpose register, +** and register 1 has traditional uses as a short-term temporary. +** +** - Added some early bailouts for OUT_OF_REGISTERS in texturing.cpp and +** GGLAssembler.cpp, since this is not fatal, and can be retried at lower +** optimization level. +** +** +** ARMAssembler and ARMAssemblerInterface changes +** +** Refactored ARM address-mode static functions (imm(), reg_imm(), imm12_pre(), etc.) +** to virtual, so they can be overridden in MIPSAssembler. The implementation of these +** functions on ARM is moved from ARMAssemblerInterface.cpp to ARMAssembler.cpp, and +** is unchanged from the original. (This required duplicating 2 of these as static +** functions in ARMAssemblerInterface.cpp so they could be used as static initializers). +*/ + + +#define LOG_TAG "MIPSAssembler" + +#include <stdio.h> +#include <stdlib.h> +#include <cutils/log.h> +#include <cutils/properties.h> + +#if defined(WITH_LIB_HARDWARE) +#include <hardware_legacy/qemu_tracing.h> +#endif + +#include <private/pixelflinger/ggl_context.h> + +#include "codeflinger/MIPSAssembler.h" +#include "codeflinger/CodeCache.h" +#include "codeflinger/mips_disassem.h" + +// Choose MIPS arch variant following gcc flags +#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2 +#define mips32r2 1 +#else +#define mips32r2 0 +#endif + + +#define NOT_IMPLEMENTED() LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__) + + + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark ArmToMipsAssembler... +#endif + +ArmToMipsAssembler::ArmToMipsAssembler(const sp<Assembly>& assembly, + char *abuf, int linesz, int instr_count) + : ARMAssemblerInterface(), + mArmDisassemblyBuffer(abuf), + mArmLineLength(linesz), + mArmInstrCount(instr_count), + mInum(0), + mAssembly(assembly) +{ + mMips = new MIPSAssembler(assembly, this); + mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *)); + init_conditional_labels(); +} + +ArmToMipsAssembler::~ArmToMipsAssembler() +{ + delete mMips; + free((void *) mArmPC); +} + +uint32_t* ArmToMipsAssembler::pc() const +{ + return mMips->pc(); +} + +uint32_t* ArmToMipsAssembler::base() const +{ + return mMips->base(); +} + +void ArmToMipsAssembler::reset() +{ + cond.labelnum = 0; + mInum = 0; + mMips->reset(); +} + +int ArmToMipsAssembler::getCodegenArch() +{ + return CODEGEN_ARCH_MIPS; +} + +void ArmToMipsAssembler::comment(const char* string) +{ + mMips->comment(string); +} + +void ArmToMipsAssembler::label(const char* theLabel) +{ + mMips->label(theLabel); +} + +void ArmToMipsAssembler::disassemble(const char* name) +{ + mMips->disassemble(name); +} + +void ArmToMipsAssembler::init_conditional_labels() +{ + int i; + for (i=0;i<99; ++i) { + sprintf(cond.label[i], "cond_%d", i); + } +} + + + +#if 0 +#pragma mark - +#pragma mark Prolog/Epilog & Generate... +#endif + +void ArmToMipsAssembler::prolog() +{ + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->ADDIU(R_sp, R_sp, -(5 * 4)); + mMips->SW(R_s0, R_sp, 0); + mMips->SW(R_s1, R_sp, 4); + mMips->SW(R_s2, R_sp, 8); + mMips->SW(R_s3, R_sp, 12); + mMips->SW(R_s4, R_sp, 16); + mMips->MOVE(R_v0, R_a0); // move context * passed in a0 to v0 (arm r0) +} + +void ArmToMipsAssembler::epilog(uint32_t touched) +{ + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->LW(R_s0, R_sp, 0); + mMips->LW(R_s1, R_sp, 4); + mMips->LW(R_s2, R_sp, 8); + mMips->LW(R_s3, R_sp, 12); + mMips->LW(R_s4, R_sp, 16); + mMips->ADDIU(R_sp, R_sp, (5 * 4)); + mMips->JR(R_ra); + +} + +int ArmToMipsAssembler::generate(const char* name) +{ + return mMips->generate(name); +} + +uint32_t* ArmToMipsAssembler::pcForLabel(const char* label) +{ + return mMips->pcForLabel(label); +} + + + +//---------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Addressing modes & shifters... +#endif + + +// do not need this for MIPS, but it is in the Interface (virtual) +int ArmToMipsAssembler::buildImmediate( + uint32_t immediate, uint32_t& rot, uint32_t& imm) +{ + // for MIPS, any 32-bit immediate is OK + rot = 0; + imm = immediate; + return 0; +} + +// shifters... + +bool ArmToMipsAssembler::isValidImmediate(uint32_t immediate) +{ + // for MIPS, any 32-bit immediate is OK + return true; +} + +uint32_t ArmToMipsAssembler::imm(uint32_t immediate) +{ + // ALOGW("immediate value %08x at pc %08x\n", immediate, (int)pc()); + amode.value = immediate; + return AMODE_IMM; +} + +uint32_t ArmToMipsAssembler::reg_imm(int Rm, int type, uint32_t shift) +{ + amode.reg = Rm; + amode.stype = type; + amode.value = shift; + return AMODE_REG_IMM; +} + +uint32_t ArmToMipsAssembler::reg_rrx(int Rm) +{ + // reg_rrx mode is not used in the GLLAssember code at this time + return AMODE_UNSUPPORTED; +} + +uint32_t ArmToMipsAssembler::reg_reg(int Rm, int type, int Rs) +{ + // reg_reg mode is not used in the GLLAssember code at this time + return AMODE_UNSUPPORTED; +} + + +// addressing modes... +// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) +uint32_t ArmToMipsAssembler::immed12_pre(int32_t immed12, int W) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + amode.value = immed12; + amode.writeback = W; + return AMODE_IMM_12_PRE; +} + +uint32_t ArmToMipsAssembler::immed12_post(int32_t immed12) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + + amode.value = immed12; + return AMODE_IMM_12_POST; +} + +uint32_t ArmToMipsAssembler::reg_scale_pre(int Rm, int type, + uint32_t shift, int W) +{ + LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented"); + + amode.reg = Rm; + // amode.stype = type; // more advanced modes not used in GGLAssembler yet + // amode.value = shift; + // amode.writeback = W; + return AMODE_REG_SCALE_PRE; +} + +uint32_t ArmToMipsAssembler::reg_scale_post(int Rm, int type, uint32_t shift) +{ + LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n"); + return AMODE_UNSUPPORTED; +} + +// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) +uint32_t ArmToMipsAssembler::immed8_pre(int32_t immed8, int W) +{ + // uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n"); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + return AMODE_IMM_8_PRE; +} + +uint32_t ArmToMipsAssembler::immed8_post(int32_t immed8) +{ + // uint32_t offset = abs(immed8); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + amode.value = immed8; + return AMODE_IMM_8_POST; +} + +uint32_t ArmToMipsAssembler::reg_pre(int Rm, int W) +{ + LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented"); + amode.reg = Rm; + return AMODE_REG_PRE; +} + +uint32_t ArmToMipsAssembler::reg_post(int Rm) +{ + LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n"); + return AMODE_UNSUPPORTED; +} + + + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Data Processing... +#endif + + +static const char * const dpOpNames[] = { + "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC", + "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN" +}; + +// check if the operand registers from a previous CMP or S-bit instruction +// would be overwritten by this instruction. If so, move the value to a +// safe register. +// Note that we cannot tell at _this_ instruction time if a future (conditional) +// instruction will _also_ use this value (a defect of the simple 1-pass, one- +// instruction-at-a-time translation). Therefore we must be conservative and +// save the value before it is overwritten. This costs an extra MOVE instr. + +void ArmToMipsAssembler::protectConditionalOperands(int Rd) +{ + if (Rd == cond.r1) { + mMips->MOVE(R_cmp, cond.r1); + cond.r1 = R_cmp; + } + if (cond.type == CMP_COND && Rd == cond.r2) { + mMips->MOVE(R_cmp2, cond.r2); + cond.r2 = R_cmp2; + } +} + + +// interprets the addressing mode, and generates the common code +// used by the majority of data-processing ops. Many MIPS instructions +// have a register-based form and a different immediate form. See +// opAND below for an example. (this could be inlined) +// +// this works with the imm(), reg_imm() methods above, which are directly +// called by the GLLAssembler. +// note: _signed parameter defaults to false (un-signed) +// note: tmpReg parameter defaults to 1, MIPS register AT +int ArmToMipsAssembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg) +{ + if (op < AMODE_REG) { + source = op; + return SRC_REG; + } else if (op == AMODE_IMM) { + if ((!_signed && amode.value > 0xffff) + || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) { + mMips->LUI(tmpReg, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff)); + } + source = tmpReg; + return SRC_REG; + } else { + source = amode.value; + return SRC_IMM; + } + } else if (op == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break; + case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break; + case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break; + case ROR: if (mips32r2) { + mMips->ROTR(tmpReg, amode.reg, amode.value); + } else { + mMips->RORIsyn(tmpReg, amode.reg, amode.value); + } + break; + } + source = tmpReg; + return SRC_REG; + } else { // adr mode RRX is not used in GGL Assembler at this time + // we are screwed, this should be exception, assert-fail or something + LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n"); + return SRC_ERROR; + } +} + + +void ArmToMipsAssembler::dataProcessing(int opcode, int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ + int src; // src is modified by dataProcAdrModes() - passed as int& + + + if (cc != AL) { + protectConditionalOperands(Rd); + // the branch tests register(s) set by prev CMP or instr with 'S' bit set + // inverse the condition to jump past this conditional instruction + ArmToMipsAssembler::B(cc^1, cond.label[++cond.labelnum]); + } else { + mArmPC[mInum++] = pc(); // save starting PC for this instr + } + + switch (opcode) { + case opAND: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->AND(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ANDI(Rd, Rn, src); + } + break; + + case opADD: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->ADDU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ADDIU(Rd, Rn, src); + } + break; + + case opSUB: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->SUBU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->SUBIU(Rd, Rn, src); + } + break; + + case opEOR: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->XOR(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->XORI(Rd, Rn, src); + } + break; + + case opORR: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->OR(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ORI(Rd, Rn, src); + } + break; + + case opBIC: + if (dataProcAdrModes(Op2, src) == SRC_IMM) { + // if we are 16-bit imnmediate, load to AT reg + mMips->ORI(R_at, 0, src); + src = R_at; + } + mMips->NOT(R_at, src); + mMips->AND(Rd, Rn, R_at); + break; + + case opRSB: + if (dataProcAdrModes(Op2, src) == SRC_IMM) { + // if we are 16-bit imnmediate, load to AT reg + mMips->ORI(R_at, 0, src); + src = R_at; + } + mMips->SUBU(Rd, src, Rn); // subu with the parameters reversed + break; + + case opMOV: + if (Op2 < AMODE_REG) { // op2 is reg # in this case + mMips->MOVE(Rd, Op2); + } else if (Op2 == AMODE_IMM) { + if (amode.value > 0xffff) { + mMips->LUI(Rd, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff)); + } + } else { + mMips->ORI(Rd, 0, amode.value); + } + } else if (Op2 == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(Rd, amode.reg, amode.value); break; + case LSR: mMips->SRL(Rd, amode.reg, amode.value); break; + case ASR: mMips->SRA(Rd, amode.reg, amode.value); break; + case ROR: if (mips32r2) { + mMips->ROTR(Rd, amode.reg, amode.value); + } else { + mMips->RORIsyn(Rd, amode.reg, amode.value); + } + break; + } + } + else { + // adr mode RRX is not used in GGL Assembler at this time + mMips->UNIMPL(); + } + break; + + case opMVN: // this is a 1's complement: NOT + if (Op2 < AMODE_REG) { // op2 is reg # in this case + mMips->NOR(Rd, Op2, 0); // NOT is NOR with 0 + break; + } else if (Op2 == AMODE_IMM) { + if (amode.value > 0xffff) { + mMips->LUI(Rd, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff)); + } + } else { + mMips->ORI(Rd, 0, amode.value); + } + } else if (Op2 == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(Rd, amode.reg, amode.value); break; + case LSR: mMips->SRL(Rd, amode.reg, amode.value); break; + case ASR: mMips->SRA(Rd, amode.reg, amode.value); break; + case ROR: if (mips32r2) { + mMips->ROTR(Rd, amode.reg, amode.value); + } else { + mMips->RORIsyn(Rd, amode.reg, amode.value); + } + break; + } + } + else { + // adr mode RRX is not used in GGL Assembler at this time + mMips->UNIMPL(); + } + mMips->NOR(Rd, Rd, 0); // NOT is NOR with 0 + break; + + case opCMP: + // Either operand of a CMP instr could get overwritten by a subsequent + // conditional instruction, which is ok, _UNLESS_ there is a _second_ + // conditional instruction. Under MIPS, this requires doing the comparison + // again (SLT), and the original operands must be available. (and this + // pattern of multiple conditional instructions from same CMP _is_ used + // in GGL-Assembler) + // + // For now, if a conditional instr overwrites the operands, we will + // move them to dedicated temp regs. This is ugly, and inefficient, + // and should be optimized. + // + // WARNING: making an _Assumption_ that CMP operand regs will NOT be + // trashed by intervening NON-conditional instructions. In the general + // case this is legal, but it is NOT currently done in GGL-Assembler. + + cond.type = CMP_COND; + cond.r1 = Rn; + if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) { + cond.r2 = src; + } else { // adr mode was SRC_IMM + mMips->ORI(R_cmp2, R_zero, src); + cond.r2 = R_cmp2; + } + + break; + + + case opTST: + case opTEQ: + case opCMN: + case opADC: + case opSBC: + case opRSC: + mMips->UNIMPL(); // currently unused in GGL Assembler code + break; + } + + if (cc != AL) { + mMips->label(cond.label[cond.labelnum]); + } + if (s && opcode != opCMP) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + + + +#if 0 +#pragma mark - +#pragma mark Multiply... +#endif + +// multiply, accumulate +void ArmToMipsAssembler::MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn) { + + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->MUL(R_at, Rm, Rs); + mMips->ADDU(Rd, R_at, Rn); + if (s) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + +void ArmToMipsAssembler::MUL(int cc, int s, + int Rd, int Rm, int Rs) { + mArmPC[mInum++] = pc(); + mMips->MUL(Rd, Rm, Rs); + if (s) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + +void ArmToMipsAssembler::UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + mArmPC[mInum++] = pc(); + mMips->MULT(Rm, Rs); + mMips->MFHI(RdHi); + mMips->MFLO(RdLo); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n"); + } +} + +void ArmToMipsAssembler::UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n"); + } +} + +void ArmToMipsAssembler::SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n"); + } +} +void ArmToMipsAssembler::SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n"); + } +} + + + +#if 0 +#pragma mark - +#pragma mark Branches... +#endif + +// branches... + +void ArmToMipsAssembler::B(int cc, const char* label) +{ + mArmPC[mInum++] = pc(); + if (cond.type == SBIT_COND) { cond.r2 = R_zero; } + + switch(cc) { + case EQ: mMips->BEQ(cond.r1, cond.r2, label); break; + case NE: mMips->BNE(cond.r1, cond.r2, label); break; + case HS: mMips->BGEU(cond.r1, cond.r2, label); break; + case LO: mMips->BLTU(cond.r1, cond.r2, label); break; + case MI: mMips->BLT(cond.r1, cond.r2, label); break; + case PL: mMips->BGE(cond.r1, cond.r2, label); break; + + case HI: mMips->BGTU(cond.r1, cond.r2, label); break; + case LS: mMips->BLEU(cond.r1, cond.r2, label); break; + case GE: mMips->BGE(cond.r1, cond.r2, label); break; + case LT: mMips->BLT(cond.r1, cond.r2, label); break; + case GT: mMips->BGT(cond.r1, cond.r2, label); break; + case LE: mMips->BLE(cond.r1, cond.r2, label); break; + case AL: mMips->B(label); break; + case NV: /* B Never - no instruction */ break; + + case VS: + case VC: + default: + LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc); + break; + } +} + +void ArmToMipsAssembler::BL(int cc, const char* label) +{ + LOG_ALWAYS_FATAL("branch-and-link not supported yet\n"); + mArmPC[mInum++] = pc(); +} + +// no use for Branches with integer PC, but they're in the Interface class .... +void ArmToMipsAssembler::B(int cc, uint32_t* to_pc) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + +void ArmToMipsAssembler::BL(int cc, uint32_t* to_pc) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + +void ArmToMipsAssembler::BX(int cc, int Rn) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + + + +#if 0 +#pragma mark - +#pragma mark Data Transfer... +#endif + +// data transfer... +void ArmToMipsAssembler::LDR(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP + } + mMips->LW(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->ADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP + } + mMips->LW(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->LW(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::LDRB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + mMips->LBU(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->ADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->LBU(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->LBU(Rd, R_at, 0); + break; + } + +} + +void ArmToMipsAssembler::STR(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP + } + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + // If we will writeback, then update the index reg, then store. + // This correctly handles stack-push case. + mMips->ADDIU(Rn, Rn, amode.value); + mMips->SW(Rd, Rn, 0); + } else { + // No writeback so store offset by value + mMips->SW(Rd, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->SW(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); // post index always writes back + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->SW(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::STRB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + mMips->SB(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->ADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->SB(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->ADDU(R_at, Rn, amode.reg); + mMips->SB(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::LDRH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed8_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + // fall thru to next case .... + case AMODE_IMM_8_PRE: // no support yet for writeback + mMips->LHU(Rd, Rn, amode.value); + break; + case AMODE_IMM_8_POST: + mMips->LHU(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_PRE: + // we only support simple base +/- index + if (amode.reg >= 0) { + mMips->ADDU(R_at, Rn, amode.reg); + } else { + mMips->SUBU(R_at, Rn, abs(amode.reg)); + } + mMips->LHU(Rd, R_at, 0); + break; + } +} + +void ArmToMipsAssembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::STRH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed8_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + // fall thru to next case .... + case AMODE_IMM_8_PRE: // no support yet for writeback + mMips->SH(Rd, Rn, amode.value); + break; + case AMODE_IMM_8_POST: + mMips->SH(Rd, Rn, 0); + mMips->ADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_PRE: + // we only support simple base +/- index + if (amode.reg >= 0) { + mMips->ADDU(R_at, Rn, amode.reg); + } else { + mMips->SUBU(R_at, Rn, abs(amode.reg)); + } + mMips->SH(Rd, R_at, 0); + break; + } +} + + + +#if 0 +#pragma mark - +#pragma mark Block Data Transfer... +#endif + +// block data transfer... +void ArmToMipsAssembler::LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // ED FD EA FA IB IA DB DA + // const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 }; + // const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 }; + // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + // (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::STM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // FA EA FD ED IB IA DB DA + // const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 }; + // const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 }; + // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + // (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + + +#if 0 +#pragma mark - +#pragma mark Special... +#endif + +// special... +void ArmToMipsAssembler::SWP(int cc, int Rn, int Rd, int Rm) { + // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::SWPB(int cc, int Rn, int Rd, int Rm) { + // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::SWI(int cc, uint32_t comment) { + // *mPC++ = (cc<<28) | (0xF<<24) | comment; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + +#if 0 +#pragma mark - +#pragma mark DSP instructions... +#endif + +// DSP instructions... +void ArmToMipsAssembler::PLD(int Rn, uint32_t offset) { + LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))), + "PLD only P=1, W=0"); + // *mPC++ = 0xF550F000 | (Rn<<16) | offset; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::CLZ(int cc, int Rd, int Rm) +{ + mArmPC[mInum++] = pc(); + mMips->CLZ(Rd, Rm); +} + +void ArmToMipsAssembler::QADD(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::QDADD(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::QSUB(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::QDSUB(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +// 16 x 16 signed multiply (like SMLAxx without the accumulate) +void ArmToMipsAssembler::SMUL(int cc, int xy, + int Rd, int Rm, int Rs) +{ + mArmPC[mInum++] = pc(); + + // the 16 bits may be in the top or bottom half of 32-bit source reg, + // as defined by the codes BB, BT, TB, TT (compressed param xy) + // where x corresponds to Rm and y to Rs + + // select half-reg for Rm + if (xy & xyTB) { + // use top 16-bits + mMips->SRA(R_at, Rm, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at, Rm); + } else { + mMips->SLL(R_at, Rm, 16); + mMips->SRA(R_at, R_at, 16); + } + } + // select half-reg for Rs + if (xy & xyBT) { + // use top 16-bits + mMips->SRA(R_at2, Rs, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at2, Rs); + } else { + mMips->SLL(R_at2, Rs, 16); + mMips->SRA(R_at2, R_at2, 16); + } + } + mMips->MUL(Rd, R_at, R_at2); +} + +// signed 32b x 16b multiple, save top 32-bits of 48-bit result +void ArmToMipsAssembler::SMULW(int cc, int y, + int Rd, int Rm, int Rs) +{ + mArmPC[mInum++] = pc(); + + // the selector yT or yB refers to reg Rs + if (y & yT) { + // zero the bottom 16-bits, with 2 shifts, it can affect result + mMips->SRL(R_at, Rs, 16); + mMips->SLL(R_at, R_at, 16); + + } else { + // move low 16-bit half, to high half + mMips->SLL(R_at, Rs, 16); + } + mMips->MULT(Rm, R_at); + mMips->MFHI(Rd); +} + +// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn +void ArmToMipsAssembler::SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn) +{ + mArmPC[mInum++] = pc(); + + // the 16 bits may be in the top or bottom half of 32-bit source reg, + // as defined by the codes BB, BT, TB, TT (compressed param xy) + // where x corresponds to Rm and y to Rs + + // select half-reg for Rm + if (xy & xyTB) { + // use top 16-bits + mMips->SRA(R_at, Rm, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at, Rm); + } else { + mMips->SLL(R_at, Rm, 16); + mMips->SRA(R_at, R_at, 16); + } + } + // select half-reg for Rs + if (xy & xyBT) { + // use top 16-bits + mMips->SRA(R_at2, Rs, 16); + } else { + // use bottom 16, but sign-extend to 32 + if (mips32r2) { + mMips->SEH(R_at2, Rs); + } else { + mMips->SLL(R_at2, Rs, 16); + mMips->SRA(R_at2, R_at2, 16); + } + } + + mMips->MUL(R_at, R_at, R_at2); + mMips->ADDU(Rd, R_at, Rn); +} + +void ArmToMipsAssembler::SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm) +{ + // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMipsAssembler::SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +// used by ARMv6 version of GGLAssembler::filter32 +void ArmToMipsAssembler::UXTB16(int cc, int Rd, int Rm, int rotate) +{ + mArmPC[mInum++] = pc(); + + //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]), + //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3. + + mMips->ROTR(Rm, Rm, rotate * 8); + mMips->AND(Rd, Rm, 0x00FF00FF); +} + +void ArmToMipsAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width) +{ + /* Placeholder for UBFX */ + mArmPC[mInum++] = pc(); + + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + + + + +#if 0 +#pragma mark - +#pragma mark MIPS Assembler... +#endif + + +//************************************************************************** +//************************************************************************** +//************************************************************************** + + +/* mips assembler +** this is a subset of mips32r2, targeted specifically at ARM instruction +** replacement in the pixelflinger/codeflinger code. +** +** To that end, there is no need for floating point, or priviledged +** instructions. This all runs in user space, no float. +** +** The syntax makes no attempt to be as complete as the assember, with +** synthetic instructions, and automatic recognition of immedate operands +** (use the immediate form of the instruction), etc. +** +** We start with mips32r1, and may add r2 and dsp extensions if cpu +** supports. Decision will be made at compile time, based on gcc +** options. (makes sense since android will be built for a a specific +** device) +*/ + +MIPSAssembler::MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent) + : mParent(parent), + mAssembly(assembly) +{ + mBase = mPC = (uint32_t *)assembly->base(); + mDuration = ggl_system_time(); +} + +MIPSAssembler::~MIPSAssembler() +{ +} + + +uint32_t* MIPSAssembler::pc() const +{ + return mPC; +} + +uint32_t* MIPSAssembler::base() const +{ + return mBase; +} + +void MIPSAssembler::reset() +{ + mBase = mPC = (uint32_t *)mAssembly->base(); + mBranchTargets.clear(); + mLabels.clear(); + mLabelsInverseMapping.clear(); + mComments.clear(); +} + + +// convert tabs to spaces, and remove any newline +// works with strings of limited size (makes a temp copy) +#define TABSTOP 8 +void MIPSAssembler::string_detab(char *s) +{ + char *os = s; + char temp[100]; + char *t = temp; + int len = 99; + int i = TABSTOP; + + while (*s && len-- > 0) { + if (*s == '\n') { s++; continue; } + if (*s == '\t') { + s++; + for ( ; i>0; i--) {*t++ = ' '; len--; } + } else { + *t++ = *s++; + } + if (i <= 0) i = TABSTOP; + i--; + } + *t = '\0'; + strcpy(os, temp); +} + +void MIPSAssembler::string_pad(char *s, int padded_len) +{ + int len = strlen(s); + s += len; + for (int i = padded_len - len; i > 0; --i) { + *s++ = ' '; + } + *s = '\0'; +} + +// ---------------------------------------------------------------------------- + +void MIPSAssembler::disassemble(const char* name) +{ + char di_buf[140]; + + if (name) { + ALOGW("%s:\n", name); + } + + bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true; + + typedef char dstr[40]; + dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer; + + if (mParent->mArmDisassemblyBuffer != NULL) { + for (int i=0; i<mParent->mArmInstrCount; ++i) { + string_detab(lines[i]); + } + } + + // iArm is an index to Arm instructions 1...n for this assembly sequence + // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS + // instruction corresponding to that Arm instruction number + + int iArm = 0; + size_t count = pc()-base(); + uint32_t* mipsPC = base(); + while (count--) { + ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC); + if (label >= 0) { + ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label)); + } + ssize_t comment = mComments.indexOfKey(mipsPC); + if (comment >= 0) { + ALOGW("; %s\n", mComments.valueAt(comment)); + } + // ALOGW("%08x: %08x ", int(i), int(i[0])); + ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt); + string_detab(di_buf); + string_pad(di_buf, 30); + ALOGW("%08x: %08x %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf); + mipsPC++; + } +} + +void MIPSAssembler::comment(const char* string) +{ + mComments.add(pc(), string); +} + +void MIPSAssembler::label(const char* theLabel) +{ + mLabels.add(theLabel, pc()); + mLabelsInverseMapping.add(pc(), theLabel); +} + + +void MIPSAssembler::prolog() +{ + // empty - done in ArmToMipsAssembler +} + +void MIPSAssembler::epilog(uint32_t touched) +{ + // empty - done in ArmToMipsAssembler +} + +int MIPSAssembler::generate(const char* name) +{ + // fixup all the branches + size_t count = mBranchTargets.size(); + while (count--) { + const branch_target_t& bt = mBranchTargets[count]; + uint32_t* target_pc = mLabels.valueFor(bt.label); + LOG_ALWAYS_FATAL_IF(!target_pc, + "error resolving branch targets, target_pc is null"); + int32_t offset = int32_t(target_pc - (bt.pc+1)); + *bt.pc |= offset & 0x00FFFF; + } + + mAssembly->resize( int(pc()-base())*4 ); + + // the instruction & data caches are flushed by CodeCache + const int64_t duration = ggl_system_time() - mDuration; + const char * const format = "generated %s (%d ins) at [%p:%p] in %lld ns\n"; + ALOGI(format, name, int(pc()-base()), base(), pc(), duration); + +#if defined(WITH_LIB_HARDWARE) + if (__builtin_expect(mQemuTracing, 0)) { + int err = qemu_add_mapping(int(base()), name); + mQemuTracing = (err >= 0); + } +#endif + + char value[PROPERTY_VALUE_MAX]; + value[0] = '\0'; + + property_get("debug.pf.disasm", value, "0"); + + if (atoi(value) != 0) { + disassemble(name); + } + + return NO_ERROR; +} + +uint32_t* MIPSAssembler::pcForLabel(const char* label) +{ + return mLabels.valueFor(label); +} + + + +#if 0 +#pragma mark - +#pragma mark Arithmetic... +#endif + +void MIPSAssembler::ADDU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (addu_fn<<FUNC_SHF) + | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + +// MD00086 pdf says this is: ADDIU rt, rs, imm -- they do not use Rd +void MIPSAssembler::ADDIU(int Rt, int Rs, int16_t imm) +{ + *mPC++ = (addiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + +void MIPSAssembler::SUBU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (subu_fn<<FUNC_SHF) | + (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ; +} + + +void MIPSAssembler::SUBIU(int Rt, int Rs, int16_t imm) // really addiu(d, s, -j) +{ + *mPC++ = (addiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16); +} + + +void MIPSAssembler::NEGU(int Rd, int Rs) // really subu(d, zero, s) +{ + MIPSAssembler::SUBU(Rd, 0, Rs); +} + +void MIPSAssembler::MUL(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec2_op<<OP_SHF) | (mul_fn<<FUNC_SHF) | + (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ; +} + +void MIPSAssembler::MULT(int Rs, int Rt) // dest is hi,lo +{ + *mPC++ = (spec_op<<OP_SHF) | (mult_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MULTU(int Rs, int Rt) // dest is hi,lo +{ + *mPC++ = (spec_op<<OP_SHF) | (multu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MADD(int Rs, int Rt) // hi,lo = hi,lo + Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (madd_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MADDU(int Rs, int Rt) // hi,lo = hi,lo + Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (maddu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + + +void MIPSAssembler::MSUB(int Rs, int Rt) // hi,lo = hi,lo - Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (msub_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MSUBU(int Rs, int Rt) // hi,lo = hi,lo - Rs * Rt +{ + *mPC++ = (spec2_op<<OP_SHF) | (msubu_fn<<FUNC_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF); +} + + +void MIPSAssembler::SEB(int Rd, int Rt) // sign-extend byte (mips32r2) +{ + *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (seb_fn << SA_SHF) | + (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + +void MIPSAssembler::SEH(int Rd, int Rt) // sign-extend half-word (mips32r2) +{ + *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (seh_fn << SA_SHF) | + (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + + + +#if 0 +#pragma mark - +#pragma mark Comparisons... +#endif + +void MIPSAssembler::SLT(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (slt_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SLTI(int Rt, int Rs, int16_t imm) +{ + *mPC++ = (slti_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + +void MIPSAssembler::SLTU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (sltu_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SLTIU(int Rt, int Rs, int16_t imm) +{ + *mPC++ = (sltiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + + +#if 0 +#pragma mark - +#pragma mark Logical... +#endif + +void MIPSAssembler::AND(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (and_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::ANDI(int Rt, int Rs, uint16_t imm) // todo: support larger immediate +{ + *mPC++ = (andi_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + + +void MIPSAssembler::OR(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (or_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::ORI(int Rt, int Rs, uint16_t imm) +{ + *mPC++ = (ori_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + +void MIPSAssembler::NOR(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (nor_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::NOT(int Rd, int Rs) +{ + MIPSAssembler::NOR(Rd, Rs, 0); // NOT(d,s) = NOR(d,s,zero) +} + +void MIPSAssembler::XOR(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (xor_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::XORI(int Rt, int Rs, uint16_t imm) // todo: support larger immediate +{ + *mPC++ = (xori_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + +void MIPSAssembler::SLL(int Rd, int Rt, int shft) +{ + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::SLLV(int Rd, int Rt, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (sllv_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SRL(int Rd, int Rt, int shft) +{ + *mPC++ = (spec_op<<OP_SHF) | (srl_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::SRLV(int Rd, int Rt, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (srlv_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::SRA(int Rd, int Rt, int shft) +{ + *mPC++ = (spec_op<<OP_SHF) | (sra_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::SRAV(int Rd, int Rt, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (srav_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::ROTR(int Rd, int Rt, int shft) // mips32r2 +{ + // note weird encoding (SRL + 1) + *mPC++ = (spec_op<<OP_SHF) | (srl_fn<<FUNC_SHF) | + (1<<RS_SHF) | (Rd<<RD_SHF) | (Rt<<RT_SHF) | (shft<<RE_SHF); +} + +void MIPSAssembler::ROTRV(int Rd, int Rt, int Rs) // mips32r2 +{ + // note weird encoding (SRLV + 1) + *mPC++ = (spec_op<<OP_SHF) | (srlv_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (1<<RE_SHF); +} + +// uses at2 register (mapped to some appropriate mips reg) +void MIPSAssembler::RORsyn(int Rd, int Rt, int Rs) +{ + // synthetic: d = t rotated by s + MIPSAssembler::NEGU(R_at2, Rs); + MIPSAssembler::SLLV(R_at2, Rt, R_at2); + MIPSAssembler::SRLV(Rd, Rt, Rs); + MIPSAssembler::OR(Rd, Rd, R_at2); +} + +// immediate version - uses at2 register (mapped to some appropriate mips reg) +void MIPSAssembler::RORIsyn(int Rd, int Rt, int rot) +{ + // synthetic: d = t rotated by immed rot + // d = s >> rot | s << (32-rot) + MIPSAssembler::SLL(R_at2, Rt, 32-rot); + MIPSAssembler::SRL(Rd, Rt, rot); + MIPSAssembler::OR(Rd, Rd, R_at2); +} + +void MIPSAssembler::CLO(int Rd, int Rs) +{ + // Rt field must have same gpr # as Rd + *mPC++ = (spec2_op<<OP_SHF) | (clo_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rd<<RT_SHF); +} + +void MIPSAssembler::CLZ(int Rd, int Rs) +{ + // Rt field must have same gpr # as Rd + *mPC++ = (spec2_op<<OP_SHF) | (clz_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rd<<RT_SHF); +} + +void MIPSAssembler::WSBH(int Rd, int Rt) // mips32r2 +{ + *mPC++ = (spec3_op<<OP_SHF) | (bshfl_fn<<FUNC_SHF) | (wsbh_fn << SA_SHF) | + (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + + + +#if 0 +#pragma mark - +#pragma mark Load/store... +#endif + +void MIPSAssembler::LW(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lw_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::SW(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (sw_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +// lb is sign-extended +void MIPSAssembler::LB(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lb_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::LBU(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lbu_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::SB(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (sb_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +// lh is sign-extended +void MIPSAssembler::LH(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lh_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::LHU(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (lhu_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::SH(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (sh_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPSAssembler::LUI(int Rt, int16_t offset) +{ + *mPC++ = (lui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + + + +#if 0 +#pragma mark - +#pragma mark Register move... +#endif + +void MIPSAssembler::MOVE(int Rd, int Rs) +{ + // encoded as "or rd, rs, zero" + *mPC++ = (spec_op<<OP_SHF) | (or_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (0<<RT_SHF); +} + +void MIPSAssembler::MOVN(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (movn_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::MOVZ(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (movz_fn<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (Rt<<RT_SHF); +} + +void MIPSAssembler::MFHI(int Rd) +{ + *mPC++ = (spec_op<<OP_SHF) | (mfhi_fn<<FUNC_SHF) | (Rd<<RD_SHF); +} + +void MIPSAssembler::MFLO(int Rd) +{ + *mPC++ = (spec_op<<OP_SHF) | (mflo_fn<<FUNC_SHF) | (Rd<<RD_SHF); +} + +void MIPSAssembler::MTHI(int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (mthi_fn<<FUNC_SHF) | (Rs<<RS_SHF); +} + +void MIPSAssembler::MTLO(int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (mtlo_fn<<FUNC_SHF) | (Rs<<RS_SHF); +} + + + +#if 0 +#pragma mark - +#pragma mark Branch... +#endif + +// temporarily forcing a NOP into branch-delay slot, just to be safe +// todo: remove NOP, optimze use of delay slots +void MIPSAssembler::B(const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + + // encoded as BEQ zero, zero, offset + *mPC++ = (beq_op<<OP_SHF) | (0<<RT_SHF) + | (0<<RS_SHF) | 0; // offset filled in later + + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BEQ(int Rs, int Rt, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (beq_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BNE(int Rs, int Rt, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (bne_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BLEZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (blez_op<<OP_SHF) | (0<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BLTZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (regimm_op<<OP_SHF) | (bltz_fn<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::BGTZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (bgtz_op<<OP_SHF) | (0<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + + +void MIPSAssembler::BGEZ(int Rs, const char* label) +{ + mBranchTargets.add(branch_target_t(label, mPC)); + *mPC++ = (regimm_op<<OP_SHF) | (bgez_fn<<RT_SHF) | (Rs<<RS_SHF) | 0; + MIPSAssembler::NOP(); +} + +void MIPSAssembler::JR(int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jr_fn << FUNC_SHF); + MIPSAssembler::NOP(); +} + + +#if 0 +#pragma mark - +#pragma mark Synthesized Branch... +#endif + +// synthetic variants of branches (using slt & friends) +void MIPSAssembler::BEQZ(int Rs, const char* label) +{ + BEQ(Rs, R_zero, label); +} + +void MIPSAssembler::BNEZ(int Rs, const char* label) +{ + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BGE(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rs, Rt); + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BGEU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rs, Rt); + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BGT(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rt, Rs); // rev + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BGTU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rt, Rs); // rev + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BLE(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rt, Rs); // rev + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BLEU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rt, Rs); // rev + BEQ(R_at, R_zero, label); +} + +void MIPSAssembler::BLT(int Rs, int Rt, const char* label) +{ + SLT(R_at, Rs, Rt); + BNE(R_at, R_zero, label); +} + +void MIPSAssembler::BLTU(int Rs, int Rt, const char* label) +{ + SLTU(R_at, Rs, Rt); + BNE(R_at, R_zero, label); +} + + + + +#if 0 +#pragma mark - +#pragma mark Misc... +#endif + +void MIPSAssembler::NOP(void) +{ + // encoded as "sll zero, zero, 0", which is all zero + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF); +} + +// using this as special opcode for not-yet-implemented ARM instruction +void MIPSAssembler::NOP2(void) +{ + // encoded as "sll zero, zero, 2", still a nop, but a unique code + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | (2 << RE_SHF); +} + +// using this as special opcode for purposefully NOT implemented ARM instruction +void MIPSAssembler::UNIMPL(void) +{ + // encoded as "sll zero, zero, 3", still a nop, but a unique code + *mPC++ = (spec_op<<OP_SHF) | (sll_fn<<FUNC_SHF) | (3 << RE_SHF); +} + + +}; // namespace android: + + diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h new file mode 100644 index 0000000..d8e8165 --- /dev/null +++ b/libpixelflinger/codeflinger/MIPSAssembler.h @@ -0,0 +1,555 @@ +/* libs/pixelflinger/codeflinger/MIPSAssembler.h +** +** Copyright 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 ANDROID_MIPSASSEMBLER_H +#define ANDROID_MIPSASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +#include "tinyutils/smartpointer.h" +#include "codeflinger/ARMAssemblerInterface.h" +#include "codeflinger/CodeCache.h" + +namespace android { + +class MIPSAssembler; // forward reference + +// this class mimics ARMAssembler interface +// intent is to translate each ARM instruction to 1 or more MIPS instr +// implementation calls MIPSAssembler class to generate mips code +class ArmToMipsAssembler : public ARMAssemblerInterface +{ +public: + ArmToMipsAssembler(const sp<Assembly>& assembly, + char *abuf = 0, int linesz = 0, int instr_count = 0); + virtual ~ArmToMipsAssembler(); + + uint32_t* base() const; + uint32_t* pc() const; + void disassemble(const char* name); + + virtual void reset(); + + virtual int generate(const char* name); + virtual int getCodegenArch(); + + virtual void prolog(); + virtual void epilog(uint32_t touched); + virtual void comment(const char* string); + + + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + // shifters... + virtual bool isValidImmediate(uint32_t immed); + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + virtual uint32_t imm(uint32_t immediate); + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift); + virtual uint32_t reg_rrx(int Rm); + virtual uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed12_pre(int32_t immed12, int W=0); + virtual uint32_t immed12_post(int32_t immed12); + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed8_pre(int32_t immed8, int W=0); + virtual uint32_t immed8_post(int32_t immed8); + virtual uint32_t reg_pre(int Rm, int W=0); + virtual uint32_t reg_post(int Rm); + + + + + virtual void dataProcessing(int opcode, int cc, int s, + int Rd, int Rn, + uint32_t Op2); + virtual void MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn); + virtual void MUL(int cc, int s, + int Rd, int Rm, int Rs); + virtual void UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + + virtual void B(int cc, uint32_t* pc); + virtual void BL(int cc, uint32_t* pc); + virtual void BX(int cc, int Rn); + virtual void label(const char* theLabel); + virtual void B(int cc, const char* label); + virtual void BL(int cc, const char* label); + + virtual uint32_t* pcForLabel(const char* label); + + virtual void LDR (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STR (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STRB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRH (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRSB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRSH(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STRH (int cc, int Rd, + int Rn, uint32_t offset = 0); + + virtual void LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + virtual void STM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + + virtual void SWP(int cc, int Rn, int Rd, int Rm); + virtual void SWPB(int cc, int Rn, int Rd, int Rm); + virtual void SWI(int cc, uint32_t comment); + + virtual void PLD(int Rn, uint32_t offset); + virtual void CLZ(int cc, int Rd, int Rm); + virtual void QADD(int cc, int Rd, int Rm, int Rn); + virtual void QDADD(int cc, int Rd, int Rm, int Rn); + virtual void QSUB(int cc, int Rd, int Rm, int Rn); + virtual void QDSUB(int cc, int Rd, int Rm, int Rn); + virtual void SMUL(int cc, int xy, + int Rd, int Rm, int Rs); + virtual void SMULW(int cc, int y, + int Rd, int Rm, int Rs); + virtual void SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn); + virtual void SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm); + virtual void SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn); + + // byte/half word extract... + virtual void UXTB16(int cc, int Rd, int Rm, int rotate); + + // bit manipulation... + virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width); + + // this is some crap to share is MIPSAssembler class for debug + char * mArmDisassemblyBuffer; + int mArmLineLength; + int mArmInstrCount; + + int mInum; // current arm instuction number (0..n) + uint32_t** mArmPC; // array: PC for 1st mips instr of + // each translated ARM instr + + +private: + ArmToMipsAssembler(const ArmToMipsAssembler& rhs); + ArmToMipsAssembler& operator = (const ArmToMipsAssembler& rhs); + + void init_conditional_labels(void); + + void protectConditionalOperands(int Rd); + + // reg__tmp set to MIPS AT, reg 1 + int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1); + + sp<Assembly> mAssembly; + MIPSAssembler* mMips; + + + enum misc_constants_t { + ARM_MAX_INSTUCTIONS = 512 // based on ASSEMBLY_SCRATCH_SIZE + }; + + enum { + SRC_REG = 0, + SRC_IMM, + SRC_ERROR = -1 + }; + + enum addr_modes { + // start above the range of legal mips reg #'s (0-31) + AMODE_REG = 0x20, + AMODE_IMM, AMODE_REG_IMM, // for data processing + AMODE_IMM_12_PRE, AMODE_IMM_12_POST, // for load/store + AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE, + AMODE_IMM_8_POST, AMODE_REG_PRE, + AMODE_UNSUPPORTED + }; + + struct addr_mode_t { // address modes for current ARM instruction + int reg; + int stype; + uint32_t value; + bool writeback; // writeback the adr reg after modification + } amode; + + enum cond_types { + CMP_COND = 1, + SBIT_COND + }; + + struct cond_mode_t { // conditional-execution info for current ARM instruction + cond_types type; + int r1; + int r2; + int labelnum; + char label[100][10]; + } cond; + +}; + + + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +// This is the basic MIPS assembler, which just creates the opcodes in memory. +// All the more complicated work is done in ArmToMipsAssember above. + +class MIPSAssembler +{ +public: + MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent); + virtual ~MIPSAssembler(); + + uint32_t* base() const; + uint32_t* pc() const; + void reset(); + + void disassemble(const char* name); + + void prolog(); + void epilog(uint32_t touched); + int generate(const char* name); + void comment(const char* string); + void label(const char* string); + + // valid only after generate() has been called + uint32_t* pcForLabel(const char* label); + + + // ------------------------------------------------------------------------ + // MIPSAssemblerInterface... + // ------------------------------------------------------------------------ + +#if 0 +#pragma mark - +#pragma mark Arithmetic... +#endif + + void ADDU(int Rd, int Rs, int Rt); + void ADDIU(int Rt, int Rs, int16_t imm); + void SUBU(int Rd, int Rs, int Rt); + void SUBIU(int Rt, int Rs, int16_t imm); + void NEGU(int Rd, int Rs); + void MUL(int Rd, int Rs, int Rt); + void MULT(int Rs, int Rt); // dest is hi,lo + void MULTU(int Rs, int Rt); // dest is hi,lo + void MADD(int Rs, int Rt); // hi,lo = hi,lo + Rs * Rt + void MADDU(int Rs, int Rt); // hi,lo = hi,lo + Rs * Rt + void MSUB(int Rs, int Rt); // hi,lo = hi,lo - Rs * Rt + void MSUBU(int Rs, int Rt); // hi,lo = hi,lo - Rs * Rt + void SEB(int Rd, int Rt); // sign-extend byte (mips32r2) + void SEH(int Rd, int Rt); // sign-extend half-word (mips32r2) + + +#if 0 +#pragma mark - +#pragma mark Comparisons... +#endif + + void SLT(int Rd, int Rs, int Rt); + void SLTI(int Rt, int Rs, int16_t imm); + void SLTU(int Rd, int Rs, int Rt); + void SLTIU(int Rt, int Rs, int16_t imm); + + +#if 0 +#pragma mark - +#pragma mark Logical... +#endif + + void AND(int Rd, int Rs, int Rt); + void ANDI(int Rd, int Rs, uint16_t imm); + void OR(int Rd, int Rs, int Rt); + void ORI(int Rt, int Rs, uint16_t imm); + void NOR(int Rd, int Rs, int Rt); + void NOT(int Rd, int Rs); + void XOR(int Rd, int Rs, int Rt); + void XORI(int Rt, int Rs, uint16_t imm); + + void SLL(int Rd, int Rt, int shft); + void SLLV(int Rd, int Rt, int Rs); + void SRL(int Rd, int Rt, int shft); + void SRLV(int Rd, int Rt, int Rs); + void SRA(int Rd, int Rt, int shft); + void SRAV(int Rd, int Rt, int Rs); + void ROTR(int Rd, int Rt, int shft); // mips32r2 + void ROTRV(int Rd, int Rt, int Rs); // mips32r2 + void RORsyn(int Rd, int Rs, int Rt); // synthetic: d = s rotated by t + void RORIsyn(int Rd, int Rt, int rot); // synthetic: d = s rotated by immed + + void CLO(int Rd, int Rs); + void CLZ(int Rd, int Rs); + void WSBH(int Rd, int Rt); + + +#if 0 +#pragma mark - +#pragma mark Load/store... +#endif + + void LW(int Rt, int Rbase, int16_t offset); + void SW(int Rt, int Rbase, int16_t offset); + void LB(int Rt, int Rbase, int16_t offset); + void LBU(int Rt, int Rbase, int16_t offset); + void SB(int Rt, int Rbase, int16_t offset); + void LH(int Rt, int Rbase, int16_t offset); + void LHU(int Rt, int Rbase, int16_t offset); + void SH(int Rt, int Rbase, int16_t offset); + void LUI(int Rt, int16_t offset); + +#if 0 +#pragma mark - +#pragma mark Register moves... +#endif + + void MOVE(int Rd, int Rs); + void MOVN(int Rd, int Rs, int Rt); + void MOVZ(int Rd, int Rs, int Rt); + void MFHI(int Rd); + void MFLO(int Rd); + void MTHI(int Rs); + void MTLO(int Rs); + +#if 0 +#pragma mark - +#pragma mark Branch... +#endif + + void B(const char* label); + void BEQ(int Rs, int Rt, const char* label); + void BNE(int Rs, int Rt, const char* label); + void BGEZ(int Rs, const char* label); + void BGTZ(int Rs, const char* label); + void BLEZ(int Rs, const char* label); + void BLTZ(int Rs, const char* label); + void JR(int Rs); + + +#if 0 +#pragma mark - +#pragma mark Synthesized Branch... +#endif + + // synthetic variants of above (using slt & friends) + void BEQZ(int Rs, const char* label); + void BNEZ(int Rs, const char* label); + void BGE(int Rs, int Rt, const char* label); + void BGEU(int Rs, int Rt, const char* label); + void BGT(int Rs, int Rt, const char* label); + void BGTU(int Rs, int Rt, const char* label); + void BLE(int Rs, int Rt, const char* label); + void BLEU(int Rs, int Rt, const char* label); + void BLT(int Rs, int Rt, const char* label); + void BLTU(int Rs, int Rt, const char* label); + +#if 0 +#pragma mark - +#pragma mark Misc... +#endif + + void NOP(void); + void NOP2(void); + void UNIMPL(void); + + + + + +private: + void string_detab(char *s); + void string_pad(char *s, int padded_len); + + ArmToMipsAssembler *mParent; + sp<Assembly> mAssembly; + uint32_t* mBase; + uint32_t* mPC; + uint32_t* mPrologPC; + int64_t mDuration; +#if defined(WITH_LIB_HARDWARE) + bool mQemuTracing; +#endif + + struct branch_target_t { + inline branch_target_t() : label(0), pc(0) { } + inline branch_target_t(const char* l, uint32_t* p) + : label(l), pc(p) { } + const char* label; + uint32_t* pc; + }; + + Vector<branch_target_t> mBranchTargets; + KeyedVector< const char*, uint32_t* > mLabels; + KeyedVector< uint32_t*, const char* > mLabelsInverseMapping; + KeyedVector< uint32_t*, const char* > mComments; + + + + + // opcode field of all instructions + enum opcode_field { + spec_op, regimm_op, j_op, jal_op, // 00 + beq_op, bne_op, blez_op, bgtz_op, + addi_op, addiu_op, slti_op, sltiu_op, // 08 + andi_op, ori_op, xori_op, lui_op, + cop0_op, cop1_op, cop2_op, cop1x_op, // 10 + beql_op, bnel_op, blezl_op, bgtzl_op, + daddi_op, daddiu_op, ldl_op, ldr_op, // 18 + spec2_op, jalx_op, mdmx_op, spec3_op, + lb_op, lh_op, lwl_op, lw_op, // 20 + lbu_op, lhu_op, lwr_op, lwu_op, + sb_op, sh_op, swl_op, sw_op, // 28 + sdl_op, sdr_op, swr_op, cache_op, + ll_op, lwc1_op, lwc2_op, pref_op, // 30 + lld_op, ldc1_op, ldc2_op, ld_op, + sc_op, swc1_op, swc2_op, rsrv_3b_op, // 38 + scd_op, sdc1_op, sdc2_op, sd_op + }; + + + // func field for special opcode + enum func_spec_op { + sll_fn, movc_fn, srl_fn, sra_fn, // 00 + sllv_fn, pmon_fn, srlv_fn, srav_fn, + jr_fn, jalr_fn, movz_fn, movn_fn, // 08 + syscall_fn, break_fn, spim_fn, sync_fn, + mfhi_fn, mthi_fn, mflo_fn, mtlo_fn, // 10 + dsllv_fn, rsrv_spec_2, dsrlv_fn, dsrav_fn, + mult_fn, multu_fn, div_fn, divu_fn, // 18 + dmult_fn, dmultu_fn, ddiv_fn, ddivu_fn, + add_fn, addu_fn, sub_fn, subu_fn, // 20 + and_fn, or_fn, xor_fn, nor_fn, + rsrv_spec_3, rsrv_spec_4, slt_fn, sltu_fn, // 28 + dadd_fn, daddu_fn, dsub_fn, dsubu_fn, + tge_fn, tgeu_fn, tlt_fn, tltu_fn, // 30 + teq_fn, rsrv_spec_5, tne_fn, rsrv_spec_6, + dsll_fn, rsrv_spec_7, dsrl_fn, dsra_fn, // 38 + dsll32_fn, rsrv_spec_8, dsrl32_fn, dsra32_fn + }; + + // func field for spec2 opcode + enum func_spec2_op { + madd_fn, maddu_fn, mul_fn, rsrv_spec2_3, + msub_fn, msubu_fn, + clz_fn = 0x20, clo_fn, + dclz_fn = 0x24, dclo_fn, + sdbbp_fn = 0x3f + }; + + // func field for spec3 opcode + enum func_spec3_op { + ext_fn, dextm_fn, dextu_fn, dext_fn, + ins_fn, dinsm_fn, dinsu_fn, dins_fn, + bshfl_fn = 0x20, + dbshfl_fn = 0x24, + rdhwr_fn = 0x3b + }; + + // sa field for spec3 opcodes, with BSHFL function + enum func_spec3_bshfl { + wsbh_fn = 0x02, + seb_fn = 0x10, + seh_fn = 0x18 + }; + + // rt field of regimm opcodes. + enum regimm_fn { + bltz_fn, bgez_fn, bltzl_fn, bgezl_fn, + rsrv_ri_fn4, rsrv_ri_fn5, rsrv_ri_fn6, rsrv_ri_fn7, + tgei_fn, tgeiu_fn, tlti_fn, tltiu_fn, + teqi_fn, rsrv_ri_fn_0d, tnei_fn, rsrv_ri_fn0f, + bltzal_fn, bgezal_fn, bltzall_fn, bgezall_fn, + bposge32_fn= 0x1c, + synci_fn = 0x1f + }; + + + // func field for mad opcodes (MIPS IV). + enum mad_func { + madd_fp_op = 0x08, msub_fp_op = 0x0a, + nmadd_fp_op = 0x0c, nmsub_fp_op = 0x0e + }; + + + enum mips_inst_shifts { + OP_SHF = 26, + JTARGET_SHF = 0, + RS_SHF = 21, + RT_SHF = 16, + RD_SHF = 11, + RE_SHF = 6, + SA_SHF = RE_SHF, // synonym + IMM_SHF = 0, + FUNC_SHF = 0, + + // mask values + MSK_16 = 0xffff, + + + CACHEOP_SHF = 18, + CACHESEL_SHF = 16, + }; +}; + +enum mips_regnames { + R_zero = 0, + R_at, R_v0, R_v1, R_a0, R_a1, R_a2, R_a3, + R_t0, R_t1, R_t2, R_t3, R_t4, R_t5, R_t6, R_t7, + R_s0, R_s1, R_s2, R_s3, R_s4, R_s5, R_s6, R_s7, + R_t8, R_t9, R_k0, R_k1, R_gp, R_sp, R_s8, R_ra, + R_lr = R_s8, + + // arm regs 0-15 are mips regs 2-17 (meaning s0 & s1 are used) + R_at2 = R_s2, // R_at2 = 18 = s2 + R_cmp = R_s3, // R_cmp = 19 = s3 + R_cmp2 = R_s4 // R_cmp2 = 20 = s4 +}; + + + +}; // namespace android + +#endif //ANDROID_MIPSASSEMBLER_H diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp index 62aa05c..146fa52 100644 --- a/libpixelflinger/codeflinger/load_store.cpp +++ b/libpixelflinger/codeflinger/load_store.cpp @@ -110,7 +110,11 @@ void GGLAssembler::extract(integer_t& d, int s, int h, int l, int bits) { const int maskLen = h-l; +#ifdef __mips__ + assert(maskLen<=11); +#else assert(maskLen<=8); +#endif assert(h); #if __ARM_ARCH__ >= 7 diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c new file mode 100644 index 0000000..4ab9bd3 --- /dev/null +++ b/libpixelflinger/codeflinger/mips_disassem.c @@ -0,0 +1,590 @@ +/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93 + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <stdbool.h> +#include <sys/cdefs.h> + +#include <sys/types.h> +#include "mips_opcode.h" + + +// #include <sys/systm.h> +// #include <sys/param.h> + +// #include <machine/reg.h> +// #include <machine/cpu.h> +/*#include <machine/param.h>*/ +// #include <machine/db_machdep.h> + +// #include <ddb/db_interface.h> +// #include <ddb/db_output.h> +// #include <ddb/db_extern.h> +// #include <ddb/db_sym.h> + + +static char *sprintf_buffer; +static int sprintf_buf_len; + + +typedef uint32_t db_addr_t; +static void db_printf(const char* fmt, ...); + +static const char * const op_name[64] = { +/* 0 */ "spec", "bcond","j ", "jal", "beq", "bne", "blez", "bgtz", +/* 8 */ "addi", "addiu","slti", "sltiu","andi", "ori", "xori", "lui", +/*16 */ "cop0", "cop1", "cop2", "cop3", "beql", "bnel", "blezl","bgtzl", +/*24 */ "daddi","daddiu","ldl", "ldr", "op34", "op35", "op36", "op37", +/*32 */ "lb ", "lh ", "lwl", "lw ", "lbu", "lhu", "lwr", "lwu", +/*40 */ "sb ", "sh ", "swl", "sw ", "sdl", "sdr", "swr", "cache", +/*48 */ "ll ", "lwc1", "lwc2", "lwc3", "lld", "ldc1", "ldc2", "ld ", +/*56 */ "sc ", "swc1", "swc2", "swc3", "scd", "sdc1", "sdc2", "sd " +}; + +static const char * const spec_name[64] = { +/* 0 */ "sll", "spec01","srl", "sra", "sllv", "spec05","srlv","srav", +/* 8 */ "jr", "jalr", "movz","movn","syscall","break","spec16","sync", +/*16 */ "mfhi", "mthi", "mflo", "mtlo", "dsllv","spec25","dsrlv","dsrav", +/*24 */ "mult", "multu","div", "divu", "dmult","dmultu","ddiv","ddivu", +/*32 */ "add", "addu", "sub", "subu", "and", "or ", "xor", "nor", +/*40 */ "spec50","spec51","slt","sltu", "dadd","daddu","dsub","dsubu", +/*48 */ "tge","tgeu","tlt","tltu","teq","spec65","tne","spec67", +/*56 */ "dsll","spec71","dsrl","dsra","dsll32","spec75","dsrl32","dsra32" +}; + +static const char * const spec2_name[64] = { /* QED RM4650, R5000, etc. */ +/* 0x00 */ "madd", "maddu", "mul", "spec3", "msub", "msubu", "rsrv6", "rsrv7", +/* 0x08 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x10 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x18 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x20 */ "clz", "clo", "rsrv", "rsrv", "dclz", "dclo", "rsrv", "rsrv", +/* 0x28 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x30 */ "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", "rsrv", +/* 0x38 */ "rsrv", "rsrv", "rsrv", "resv", "rsrv", "rsrv", "rsrv", "sdbbp" +}; + +static const char * const bcond_name[32] = { +/* 0 */ "bltz", "bgez", "bltzl", "bgezl", "?", "?", "?", "?", +/* 8 */ "tgei", "tgeiu", "tlti", "tltiu", "teqi", "?", "tnei", "?", +/*16 */ "bltzal", "bgezal", "bltzall", "bgezall", "?", "?", "?", "?", +/*24 */ "?", "?", "?", "?", "?", "?", "?", "?", +}; + +static const char * const cop1_name[64] = { +/* 0 */ "fadd", "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg", +/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f", +/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17", +/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f", +/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27", +/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f", +/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult", + "fcmp.ole","fcmp.ule", +/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge", + "fcmp.le","fcmp.ngt" +}; + +static const char * const fmt_name[16] = { + "s", "d", "e", "fmt3", + "w", "fmt5", "fmt6", "fmt7", + "fmt8", "fmt9", "fmta", "fmtb", + "fmtc", "fmtd", "fmte", "fmtf" +}; + +#if defined(__mips_n32) || defined(__mips_n64) +static char * const reg_name[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; +#else + +static char * alt_arm_reg_name[32] = { // hacked names for comparison with ARM code + "zero", "at", "r0", "r1", "r2", "r3", "r4", "r5", + "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", + "r14", "r15", "at2", "cmp", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static char * mips_reg_name[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static char ** reg_name = &mips_reg_name[0]; + +#endif /* __mips_n32 || __mips_n64 */ + +static const char * const c0_opname[64] = { + "c0op00","tlbr", "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07", + "tlbp", "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17", + "rfe", "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27", + "eret", "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37", + "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47", + "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57", + "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67", + "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77", +}; + +static const char * const c0_reg[32] = { + "index", "random", "tlblo0", "tlblo1", + "context", "pagemask", "wired", "cp0r7", + "badvaddr", "count", "tlbhi", "compare", + "status", "cause", "epc", "prid", + "config", "lladdr", "watchlo", "watchhi", + "xcontext", "cp0r21", "cp0r22", "debug", + "depc", "perfcnt", "ecc", "cacheerr", + "taglo", "taghi", "errepc", "desave" +}; + +static void print_addr(db_addr_t); +db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format); + + +/* + * Disassemble instruction 'insn' nominally at 'loc'. + * 'loc' may in fact contain a breakpoint instruction. + */ +static db_addr_t +db_disasm_insn(int insn, db_addr_t loc, bool altfmt) +{ + bool bdslot = false; + InstFmt i; + + i.word = insn; + + switch (i.JType.op) { + case OP_SPECIAL: + if (i.word == 0) { + db_printf("nop"); + break; + } + if (i.word == 0x0080) { + db_printf("NIY"); + break; + } + if (i.word == 0x00c0) { + db_printf("NOT IMPL"); + break; + } + /* Special cases -------------------------------------------------- + * "addu" is a "move" only in 32-bit mode. What's the correct + * answer - never decode addu/daddu as "move"? + */ + if ( (i.RType.func == OP_ADDU && i.RType.rt == 0) || + (i.RType.func == OP_OR && i.RType.rt == 0) ) { + db_printf("move\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rs]); + break; + } + // mips32r2, rotr & rotrv + if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) { + db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd], + reg_name[i.RType.rt], i.RType.shamt); + break; + } + if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) { + db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd], + reg_name[i.RType.rt], reg_name[i.RType.rs]); + break; + } + + + db_printf("%s", spec_name[i.RType.func]); + switch (i.RType.func) { + case OP_SLL: + case OP_SRL: + case OP_SRA: + case OP_DSLL: + + case OP_DSRL: + case OP_DSRA: + case OP_DSLL32: + case OP_DSRL32: + case OP_DSRA32: + db_printf("\t%s,%s,%d", + reg_name[i.RType.rd], + reg_name[i.RType.rt], + i.RType.shamt); + break; + + case OP_SLLV: + case OP_SRLV: + case OP_SRAV: + case OP_DSLLV: + case OP_DSRLV: + case OP_DSRAV: + db_printf("\t%s,%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt], + reg_name[i.RType.rs]); + break; + + case OP_MFHI: + case OP_MFLO: + db_printf("\t%s", reg_name[i.RType.rd]); + break; + + case OP_JR: + case OP_JALR: + db_printf("\t%s", reg_name[i.RType.rs]); + bdslot = true; + break; + case OP_MTLO: + case OP_MTHI: + db_printf("\t%s", reg_name[i.RType.rs]); + break; + + case OP_MULT: + case OP_MULTU: + case OP_DMULT: + case OP_DMULTU: + case OP_DIV: + case OP_DIVU: + case OP_DDIV: + case OP_DDIVU: + db_printf("\t%s,%s", + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + break; + + + case OP_SYSCALL: + case OP_SYNC: + break; + + case OP_BREAK: + db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt); + break; + + default: + db_printf("\t%s,%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + } + break; + + case OP_SPECIAL2: + if (i.RType.func == OP_MUL) + db_printf("%s\t%s,%s,%s", + spec2_name[i.RType.func & 0x3f], + reg_name[i.RType.rd], + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + else + db_printf("%s\t%s,%s", + spec2_name[i.RType.func & 0x3f], + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + + break; + + case OP_SPECIAL3: + if (i.RType.func == OP_EXT) + db_printf("ext\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.rd+1, + i.RType.shamt); + else if (i.RType.func == OP_INS) + db_printf("ins\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.rd+1, + i.RType.shamt); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH) + db_printf("wsbh\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB) + db_printf("seb\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH) + db_printf("seh\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else + db_printf("Unknown"); + break; + + case OP_BCOND: + db_printf("%s\t%s,", bcond_name[i.IType.rt], + reg_name[i.IType.rs]); + goto pr_displ; + + case OP_BLEZ: + case OP_BLEZL: + case OP_BGTZ: + case OP_BGTZL: + db_printf("%s\t%s,", op_name[i.IType.op], + reg_name[i.IType.rs]); + goto pr_displ; + + case OP_BEQ: + case OP_BEQL: + if (i.IType.rs == 0 && i.IType.rt == 0) { + db_printf("b \t"); + goto pr_displ; + } + /* FALLTHROUGH */ + case OP_BNE: + case OP_BNEL: + db_printf("%s\t%s,%s,", op_name[i.IType.op], + reg_name[i.IType.rs], + reg_name[i.IType.rt]); + pr_displ: + print_addr(loc + 4 + ((short)i.IType.imm << 2)); + bdslot = true; + break; + + case OP_COP0: + switch (i.RType.rs) { + case OP_BCx: + case OP_BCy: + + db_printf("bc0%c\t", + "ft"[i.RType.rt & COPz_BC_TF_MASK]); + goto pr_displ; + + case OP_MT: + db_printf("mtc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_DMT: + db_printf("dmtc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_MF: + db_printf("mfc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_DMF: + db_printf("dmfc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + default: + db_printf("%s", c0_opname[i.FRType.func]); + } + break; + + case OP_COP1: + switch (i.RType.rs) { + case OP_BCx: + case OP_BCy: + db_printf("bc1%c\t", + "ft"[i.RType.rt & COPz_BC_TF_MASK]); + goto pr_displ; + + case OP_MT: + db_printf("mtc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_MF: + db_printf("mfc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_CT: + db_printf("ctc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_CF: + db_printf("cfc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + default: + db_printf("%s.%s\tf%d,f%d,f%d", + cop1_name[i.FRType.func], + fmt_name[i.FRType.fmt], + i.FRType.fd, i.FRType.fs, i.FRType.ft); + } + break; + + case OP_J: + case OP_JAL: + db_printf("%s\t", op_name[i.JType.op]); + print_addr((loc & 0xF0000000) | (i.JType.target << 2)); + bdslot = true; + break; + + case OP_LWC1: + case OP_SWC1: + db_printf("%s\tf%d,", op_name[i.IType.op], + i.IType.rt); + goto loadstore; + + case OP_LB: + case OP_LH: + case OP_LW: + case OP_LD: + case OP_LBU: + case OP_LHU: + case OP_LWU: + case OP_SB: + case OP_SH: + case OP_SW: + case OP_SD: + db_printf("%s\t%s,", op_name[i.IType.op], + reg_name[i.IType.rt]); + loadstore: + db_printf("%d(%s)", (short)i.IType.imm, + reg_name[i.IType.rs]); + break; + + case OP_ORI: + case OP_XORI: + if (i.IType.rs == 0) { + db_printf("li\t%s,0x%x", + reg_name[i.IType.rt], + i.IType.imm); + break; + } + /* FALLTHROUGH */ + case OP_ANDI: + db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op], + reg_name[i.IType.rt], + reg_name[i.IType.rs], + i.IType.imm); + break; + + case OP_LUI: + db_printf("%s\t%s,0x%x", op_name[i.IType.op], + reg_name[i.IType.rt], + i.IType.imm); + break; + + case OP_CACHE: + db_printf("%s\t0x%x,0x%x(%s)", + op_name[i.IType.op], + i.IType.rt, + i.IType.imm, + reg_name[i.IType.rs]); + break; + + case OP_ADDI: + case OP_DADDI: + case OP_ADDIU: + case OP_DADDIU: + if (i.IType.rs == 0) { + db_printf("li\t%s,%d", + reg_name[i.IType.rt], + (short)i.IType.imm); + break; + } + /* FALLTHROUGH */ + default: + db_printf("%s\t%s,%s,%d", op_name[i.IType.op], + reg_name[i.IType.rt], + reg_name[i.IType.rs], + (short)i.IType.imm); + } + // db_printf("\n"); + // if (bdslot) { + // db_printf(" bd: "); + // mips_disassem(loc+4); + // return (loc + 8); + // } + return (loc + 4); +} + +static void +print_addr(db_addr_t loc) +{ + db_printf("0x%08x", loc); +} + + + +static void db_printf(const char* fmt, ...) +{ + int cnt; + va_list argp; + va_start(argp, fmt); + if (sprintf_buffer) { + cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp); + sprintf_buffer += cnt; + sprintf_buf_len -= cnt; + } else { + vprintf(fmt, argp); + } +} + + +/* + * Disassemble instruction at 'loc'. + * Return address of start of next instruction. + * Since this function is used by 'examine' and by 'step' + * "next instruction" does NOT mean the next instruction to + * be executed but the 'linear' next instruction. + */ +db_addr_t +mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format) +{ + u_int32_t instr; + + if (alt_dis_format) { // use ARM register names for disassembly + reg_name = &alt_arm_reg_name[0]; + } + + sprintf_buffer = di_buffer; // quick 'n' dirty printf() vs sprintf() + sprintf_buf_len = 39; // should be passed in + + instr = *(u_int32_t *)loc; + return (db_disasm_insn(instr, loc, false)); +} + diff --git a/libpixelflinger/codeflinger/mips_disassem.h b/libpixelflinger/codeflinger/mips_disassem.h new file mode 100644 index 0000000..2d5b7f5 --- /dev/null +++ b/libpixelflinger/codeflinger/mips_disassem.h @@ -0,0 +1,66 @@ +/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93 + */ + + + +#ifndef ANDROID_MIPS_DISASSEM_H +#define ANDROID_MIPS_DISASSEM_H + +#include <sys/types.h> + +#if __cplusplus +extern "C" { +#endif + + +// could add an interface like this, but I have not +// typedef struct { +// u_int (*di_readword)(u_int); +// void (*di_printaddr)(u_int); +// void (*di_printf)(const char *, ...); +// } disasm_interface_t; + +/* Prototypes for callable functions */ + +// u_int disasm(const disasm_interface_t *, u_int, int); + +void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt); + +#if __cplusplus +} +#endif + +#endif /* !ANDROID_MIPS_DISASSEM_H */ diff --git a/libpixelflinger/codeflinger/mips_opcode.h b/libpixelflinger/codeflinger/mips_opcode.h new file mode 100644 index 0000000..7ed5ef5 --- /dev/null +++ b/libpixelflinger/codeflinger/mips_opcode.h @@ -0,0 +1,316 @@ +/* $NetBSD: mips_opcode.h,v 1.12 2005/12/11 12:18:09 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mips_opcode.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Define the instruction formats and opcode values for the + * MIPS instruction set. + */ + +#include <endian.h> + +/* + * Define the instruction formats. + */ +typedef union { + unsigned word; + +#if BYTE_ORDER == LITTLE_ENDIAN + struct { + unsigned imm: 16; + unsigned rt: 5; + unsigned rs: 5; + unsigned op: 6; + } IType; + + struct { + unsigned target: 26; + unsigned op: 6; + } JType; + + struct { + unsigned func: 6; + unsigned shamt: 5; + unsigned rd: 5; + unsigned rt: 5; + unsigned rs: 5; + unsigned op: 6; + } RType; + + struct { + unsigned func: 6; + unsigned fd: 5; + unsigned fs: 5; + unsigned ft: 5; + unsigned fmt: 4; + unsigned : 1; /* always '1' */ + unsigned op: 6; /* always '0x11' */ + } FRType; +#endif +#if BYTE_ORDER == BIG_ENDIAN + struct { + unsigned op: 6; + unsigned rs: 5; + unsigned rt: 5; + unsigned imm: 16; + } IType; + + struct { + unsigned op: 6; + unsigned target: 26; + } JType; + + struct { + unsigned op: 6; + unsigned rs: 5; + unsigned rt: 5; + unsigned rd: 5; + unsigned shamt: 5; + unsigned func: 6; + } RType; + + struct { + unsigned op: 6; /* always '0x11' */ + unsigned : 1; /* always '1' */ + unsigned fmt: 4; + unsigned ft: 5; + unsigned fs: 5; + unsigned fd: 5; + unsigned func: 6; + } FRType; +#endif +} InstFmt; + +/* + * Values for the 'op' field. + */ +#define OP_SPECIAL 000 +#define OP_BCOND 001 +#define OP_J 002 +#define OP_JAL 003 +#define OP_BEQ 004 +#define OP_BNE 005 +#define OP_BLEZ 006 +#define OP_BGTZ 007 + +#define OP_ADDI 010 +#define OP_ADDIU 011 +#define OP_SLTI 012 +#define OP_SLTIU 013 +#define OP_ANDI 014 +#define OP_ORI 015 +#define OP_XORI 016 +#define OP_LUI 017 + +#define OP_COP0 020 +#define OP_COP1 021 +#define OP_COP2 022 +#define OP_COP3 023 +#define OP_BEQL 024 /* MIPS-II, for r4000 port */ +#define OP_BNEL 025 /* MIPS-II, for r4000 port */ +#define OP_BLEZL 026 /* MIPS-II, for r4000 port */ +#define OP_BGTZL 027 /* MIPS-II, for r4000 port */ + +#define OP_DADDI 030 /* MIPS-II, for r4000 port */ +#define OP_DADDIU 031 /* MIPS-II, for r4000 port */ +#define OP_LDL 032 /* MIPS-II, for r4000 port */ +#define OP_LDR 033 /* MIPS-II, for r4000 port */ + +#define OP_SPECIAL2 034 /* QED opcodes */ +#define OP_SPECIAL3 037 /* mips32r2 opcodes */ + +#define OP_LB 040 +#define OP_LH 041 +#define OP_LWL 042 +#define OP_LW 043 +#define OP_LBU 044 +#define OP_LHU 045 +#define OP_LWR 046 +#define OP_LHU 045 +#define OP_LWR 046 +#define OP_LWU 047 /* MIPS-II, for r4000 port */ + +#define OP_SB 050 +#define OP_SH 051 +#define OP_SWL 052 +#define OP_SW 053 +#define OP_SDL 054 /* MIPS-II, for r4000 port */ +#define OP_SDR 055 /* MIPS-II, for r4000 port */ +#define OP_SWR 056 +#define OP_CACHE 057 /* MIPS-II, for r4000 port */ + +#define OP_LL 060 +#define OP_LWC0 OP_LL /* backwards source compatibility */ +#define OP_LWC1 061 +#define OP_LWC2 062 +#define OP_LWC3 063 +#define OP_LLD 064 /* MIPS-II, for r4000 port */ +#define OP_LDC1 065 +#define OP_LD 067 /* MIPS-II, for r4000 port */ + +#define OP_SC 070 +#define OP_SWC0 OP_SC /* backwards source compatibility */ +#define OP_SWC1 071 +#define OP_SWC2 072 +#define OP_SWC3 073 +#define OP_SCD 074 /* MIPS-II, for r4000 port */ +#define OP_SDC1 075 +#define OP_SD 077 /* MIPS-II, for r4000 port */ + +/* + * Values for the 'func' field when 'op' == OP_SPECIAL. + */ +#define OP_SLL 000 +#define OP_SRL 002 +#define OP_SRA 003 +#define OP_SLLV 004 +#define OP_SRLV 006 +#define OP_SRAV 007 + +#define OP_JR 010 +#define OP_JALR 011 +#define OP_SYSCALL 014 +#define OP_BREAK 015 +#define OP_SYNC 017 /* MIPS-II, for r4000 port */ + +#define OP_MFHI 020 +#define OP_MTHI 021 +#define OP_MFLO 022 +#define OP_MTLO 023 +#define OP_DSLLV 024 /* MIPS-II, for r4000 port */ +#define OP_DSRLV 026 /* MIPS-II, for r4000 port */ +#define OP_DSRAV 027 /* MIPS-II, for r4000 port */ + +#define OP_MULT 030 +#define OP_MULTU 031 +#define OP_DIV 032 +#define OP_DIVU 033 +#define OP_DMULT 034 /* MIPS-II, for r4000 port */ +#define OP_DMULTU 035 /* MIPS-II, for r4000 port */ +#define OP_DDIV 036 /* MIPS-II, for r4000 port */ +#define OP_DDIVU 037 /* MIPS-II, for r4000 port */ + +#define OP_ADD 040 +#define OP_ADDU 041 +#define OP_SUB 042 +#define OP_SUBU 043 +#define OP_AND 044 +#define OP_OR 045 +#define OP_XOR 046 +#define OP_NOR 047 + +#define OP_SLT 052 +#define OP_SLTU 053 +#define OP_DADD 054 /* MIPS-II, for r4000 port */ +#define OP_DADDU 055 /* MIPS-II, for r4000 port */ +#define OP_DSUB 056 /* MIPS-II, for r4000 port */ +#define OP_DSUBU 057 /* MIPS-II, for r4000 port */ + +#define OP_TGE 060 /* MIPS-II, for r4000 port */ +#define OP_TGEU 061 /* MIPS-II, for r4000 port */ +#define OP_TLT 062 /* MIPS-II, for r4000 port */ +#define OP_TLTU 063 /* MIPS-II, for r4000 port */ +#define OP_TEQ 064 /* MIPS-II, for r4000 port */ +#define OP_TNE 066 /* MIPS-II, for r4000 port */ + +#define OP_DSLL 070 /* MIPS-II, for r4000 port */ +#define OP_DSRL 072 /* MIPS-II, for r4000 port */ +#define OP_DSRA 073 /* MIPS-II, for r4000 port */ +#define OP_DSLL32 074 /* MIPS-II, for r4000 port */ +#define OP_DSRL32 076 /* MIPS-II, for r4000 port */ +#define OP_DSRA32 077 /* MIPS-II, for r4000 port */ + +/* + * Values for the 'func' field when 'op' == OP_SPECIAL2. + */ +#define OP_MAD 000 /* QED */ +#define OP_MADU 001 /* QED */ +#define OP_MUL 002 /* QED */ + +/* + * Values for the 'func' field when 'op' == OP_SPECIAL3. + */ +#define OP_EXT 000 +#define OP_INS 004 +#define OP_BSHFL 040 + +/* + * Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL. + */ +#define OP_WSBH 002 +#define OP_SEB 020 +#define OP_SEH 030 + +/* + * Values for the 'func' field when 'op' == OP_BCOND. + */ +#define OP_BLTZ 000 +#define OP_BGEZ 001 +#define OP_BLTZL 002 /* MIPS-II, for r4000 port */ +#define OP_BGEZL 003 /* MIPS-II, for r4000 port */ + +#define OP_TGEI 010 /* MIPS-II, for r4000 port */ +#define OP_TGEIU 011 /* MIPS-II, for r4000 port */ +#define OP_TLTI 012 /* MIPS-II, for r4000 port */ +#define OP_TLTIU 013 /* MIPS-II, for r4000 port */ +#define OP_TEQI 014 /* MIPS-II, for r4000 port */ +#define OP_TNEI 016 /* MIPS-II, for r4000 port */ + +#define OP_BLTZAL 020 /* MIPS-II, for r4000 port */ +#define OP_BGEZAL 021 +#define OP_BLTZALL 022 +#define OP_BGEZALL 023 + +/* + * Values for the 'rs' field when 'op' == OP_COPz. + */ +#define OP_MF 000 +#define OP_DMF 001 /* MIPS-II, for r4000 port */ +#define OP_MT 004 +#define OP_DMT 005 /* MIPS-II, for r4000 port */ +#define OP_BCx 010 +#define OP_BCy 014 +#define OP_CF 002 +#define OP_CT 006 + +/* + * Values for the 'rt' field when 'op' == OP_COPz. + */ +#define COPz_BC_TF_MASK 0x01 +#define COPz_BC_TRUE 0x01 +#define COPz_BC_FALSE 0x00 +#define COPz_BCL_TF_MASK 0x02 /* MIPS-II, for r4000 port */ +#define COPz_BCL_TRUE 0x02 /* MIPS-II, for r4000 port */ +#define COPz_BCL_FALSE 0x00 /* MIPS-II, for r4000 port */ diff --git a/libpixelflinger/codeflinger/texturing.cpp b/libpixelflinger/codeflinger/texturing.cpp index 8464fbd..4d5a50f 100644 --- a/libpixelflinger/codeflinger/texturing.cpp +++ b/libpixelflinger/codeflinger/texturing.cpp @@ -464,6 +464,9 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, CONTEXT_LOAD(t.reg, generated_vars.texture[i].spill[1]); } + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + comment("compute repeat/clamp"); int u = scratches.obtain(); int v = scratches.obtain(); @@ -472,6 +475,9 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, int U = 0; int V = 0; + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + CONTEXT_LOAD(width, generated_vars.texture[i].width); CONTEXT_LOAD(height, generated_vars.texture[i].height); @@ -510,6 +516,9 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, U = scratches.obtain(); V = scratches.obtain(); + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + // sample the texel center SUB(AL, 0, u, u, imm(1<<(FRAC_BITS-1))); SUB(AL, 0, v, v, imm(1<<(FRAC_BITS-1))); @@ -593,6 +602,10 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, comment("iterate s,t"); int dsdx = scratches.obtain(); int dtdx = scratches.obtain(); + + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx); CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx); ADD(AL, 0, s.reg, s.reg, dsdx); @@ -611,6 +624,10 @@ void GGLAssembler::build_textures( fragment_parts_t& parts, texel.setTo(regs.obtain(), &tmu.format); txPtr.setTo(texel.reg, tmu.bits); int stride = scratches.obtain(); + + if (registerFile().status() & RegisterFile::OUT_OF_REGISTERS) + return; + CONTEXT_LOAD(stride, generated_vars.texture[i].stride); CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); SMLABB(AL, u, v, stride, u); // u+v*stride @@ -1078,6 +1095,7 @@ void GGLAssembler::build_texture_environment( Scratch scratches(registerFile()); pixel_t texel(parts.texel[i]); + if (multiTexture && tmu.swrap == GGL_NEEDS_WRAP_11 && tmu.twrap == GGL_NEEDS_WRAP_11) diff --git a/libpixelflinger/fixed.cpp b/libpixelflinger/fixed.cpp index 5b92062..5094537 100644 --- a/libpixelflinger/fixed.cpp +++ b/libpixelflinger/fixed.cpp @@ -62,7 +62,8 @@ int32_t gglRecipQ(GGLfixed x, int q) int shift; x = gglRecipQNormalized(x, &shift); shift += 16-q; - x += 1L << (shift-1); // rounding + if (shift > 0) + x += 1L << (shift-1); // rounding x >>= shift; return x; } diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index 93440f5..a5d28b2 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -32,6 +32,9 @@ #include "codeflinger/CodeCache.h" #include "codeflinger/GGLAssembler.h" #include "codeflinger/ARMAssembler.h" +#if defined(__mips__) +#include "codeflinger/MIPSAssembler.h" +#endif //#include "codeflinger/ARMAssemblerOptimizer.h" // ---------------------------------------------------------------------------- @@ -49,7 +52,7 @@ # define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED #endif -#if defined(__arm__) +#if defined(__arm__) || defined(__mips__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 @@ -63,7 +66,11 @@ */ #define DEBUG_NEEDS 0 +#ifdef __mips__ +#define ASSEMBLY_SCRATCH_SIZE 4096 +#else #define ASSEMBLY_SCRATCH_SIZE 2048 +#endif // ---------------------------------------------------------------------------- namespace android { @@ -110,10 +117,14 @@ static void scanline_clear(context_t* c); static void rect_generic(context_t* c, size_t yc); static void rect_memcpy(context_t* c, size_t yc); +#if defined( __arm__) extern "C" void scanline_t32cb16blend_arm(uint16_t*, uint32_t*, size_t); extern "C" void scanline_t32cb16_arm(uint16_t *dst, uint32_t *src, size_t ct); extern "C" void scanline_col32cb16blend_neon(uint16_t *dst, uint32_t *col, size_t ct); extern "C" void scanline_col32cb16blend_arm(uint16_t *dst, uint32_t col, size_t ct); +#elif defined(__mips__) +extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t); +#endif // ---------------------------------------------------------------------------- @@ -262,7 +273,12 @@ static const needs_filter_t fill16noblend = { // ---------------------------------------------------------------------------- #if ANDROID_ARM_CODEGEN + +#if defined(__mips__) +static CodeCache gCodeCache(32 * 1024); +#else static CodeCache gCodeCache(12 * 1024); +#endif class ScanlineAssembly : public Assembly { AssemblyKey<needs_t> mKey; @@ -371,9 +387,14 @@ static void pick_scanline(context_t* c) sp<ScanlineAssembly> a = new ScanlineAssembly(c->state.needs, ASSEMBLY_SCRATCH_SIZE); // initialize our assembler +#if defined(__arm__) GGLAssembler assembler( new ARMAssembler(a) ); //GGLAssembler assembler( // new ARMAssemblerOptimizer(new ARMAssembler(a)) ); +#endif +#if defined(__mips__) + GGLAssembler assembler( new ArmToMipsAssembler(a) ); +#endif // generate the scanline code for the given needs int err = assembler.scanline(c->state.needs, c); if (ggl_likely(!err)) { @@ -2136,7 +2157,7 @@ last_one: void scanline_t32cb16blend(context_t* c) { -#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__arm__)) +#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__mips))) int32_t x = c->iterators.xl; size_t ct = c->iterators.xr - x; int32_t y = c->iterators.y; @@ -2148,8 +2169,12 @@ void scanline_t32cb16blend(context_t* c) const int32_t v = (c->state.texture[0].shade.it0>>16) + y; uint32_t *src = reinterpret_cast<uint32_t*>(tex->data)+(u+(tex->stride*v)); +#ifdef __arm__ scanline_t32cb16blend_arm(dst, src, ct); #else + scanline_t32cb16blend_mips(dst, src, ct); +#endif +#else dst_iterator16 di(c); horz_iterator32 hi(c); blender_32to16 bl(c); diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp index 94e2481..3d5a040 100644 --- a/libpixelflinger/tests/codegen/codegen.cpp +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -9,14 +9,19 @@ #include "codeflinger/CodeCache.h" #include "codeflinger/GGLAssembler.h" #include "codeflinger/ARMAssembler.h" +#include "codeflinger/MIPSAssembler.h" -#if defined(__arm__) +#if defined(__arm__) || defined(__mips__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 #endif +#if defined (__mips__) +#define ASSEMBLY_SCRATCH_SIZE 4096 +#else #define ASSEMBLY_SCRATCH_SIZE 2048 +#endif using namespace android; @@ -39,14 +44,22 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) needs.t[0] = t0; needs.t[1] = t1; sp<ScanlineAssembly> a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); + +#if defined(__arm__) GGLAssembler assembler( new ARMAssembler(a) ); +#endif + +#if defined(__mips__) + GGLAssembler assembler( new ArmToMipsAssembler(a) ); +#endif + int err = assembler.scanline(needs, (context_t*)c); if (err != 0) { printf("error %08x (%s)\n", err, strerror(-err)); } gglUninit(c); #else - printf("This test runs only on ARM\n"); + printf("This test runs only on ARM or MIPS\n"); #endif } diff --git a/libsparse/Android.mk b/libsparse/Android.mk new file mode 100644 index 0000000..9025cc0 --- /dev/null +++ b/libsparse/Android.mk @@ -0,0 +1,99 @@ +# 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_host +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_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_static +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_host +# Need a unique module name, but exe should still be called simg2img +LOCAL_MODULE_STEM := simg2img +LOCAL_STATIC_LIBRARIES := \ + libsparse_host \ + libz +include $(BUILD_HOST_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := simg2img.c \ + sparse_crc32.c +LOCAL_MODULE := simg2img +LOCAL_STATIC_LIBRARIES := \ + libsparse_static \ + libz +include $(BUILD_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := img2simg.c +LOCAL_MODULE := img2simg_host +# Need a unique module name, but exe should still be called simg2img +LOCAL_MODULE_STEM := img2simg +LOCAL_STATIC_LIBRARIES := \ + libsparse_host \ + libz +include $(BUILD_HOST_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := img2simg.c +LOCAL_MODULE := img2simg +LOCAL_STATIC_LIBRARIES := \ + libsparse_static \ + libz +include $(BUILD_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := simg2simg.c +LOCAL_MODULE := simg2simg +LOCAL_STATIC_LIBRARIES := \ + libsparse_host \ + libz +include $(BUILD_HOST_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_MODULE := simg_dump.py +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..5014e4a --- /dev/null +++ b/libsparse/output_file.c @@ -0,0 +1,775 @@ +/* + * 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(); + } + if (!out) { + return NULL; + } + + 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.h b/libsparse/sparse_crc32.h index a7f944a..cad8a86 100644 --- a/nexus/NexusCommand.h +++ 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,15 +14,7 @@ * limitations under the License. */ -#ifndef _NEXUS_COMMAND_H -#define _NEXUS_COMMAND_H +#include <stdint.h> -#include <sysutils/FrameworkCommand.h> +uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size); -class NexusCommand : public FrameworkCommand { -public: - NexusCommand(const char *cmd); - virtual ~NexusCommand() {} -}; - -#endif 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/DhcpEvent.h b/libsparse/sparse_err.c index f77834d..0f392ad 100644 --- a/nexus/DhcpEvent.h +++ 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,20 +14,20 @@ * limitations under the License. */ -#ifndef _DHCP_EVENT_H -#define _DHCP_EVENT_H +#include <sparse/sparse.h> -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 <stdarg.h> +#include <stdio.h> +#include <unistd.h> - static char *toString(int val, char *buffer, int max); +void sparse_default_print(const char *fmt, ...) +{ + va_list argp; - static int parseString(const char *buffer); -}; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); +} -#endif +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..a2fa3e0 --- /dev/null +++ b/libsuspend/Android.mk @@ -0,0 +1,31 @@ +# 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) + +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_CFLAGS += -DLOG_NDEBUG=0 +include $(BUILD_STATIC_LIBRARY) diff --git a/libsuspend/autosuspend.c b/libsuspend/autosuspend.c new file mode 100644 index 0000000..eb1f66e --- /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_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: + autosuspend_inited = true; + + 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..5451615 --- /dev/null +++ b/libsuspend/autosuspend_autosleep.c @@ -0,0 +1,102 @@ +/* + * 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"); + + autosuspend_autosleep_disable(); + + return &autosuspend_autosleep_ops; +} diff --git a/libsuspend/autosuspend_earlysuspend.c b/libsuspend/autosuspend_earlysuspend.c new file mode 100644 index 0000000..1df8c6a --- /dev/null +++ b/libsuspend/autosuspend_earlysuspend.c @@ -0,0 +1,227 @@ +/* + * 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 <stdbool.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 pthread_t earlysuspend_thread; +static pthread_mutex_t earlysuspend_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t earlysuspend_cond = PTHREAD_COND_INITIALIZER; +static bool wait_for_earlysuspend; +static enum { + EARLYSUSPEND_ON, + EARLYSUSPEND_MEM, +} earlysuspend_state = EARLYSUSPEND_ON; + +int wait_for_fb_wake(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_WAKE, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static int wait_for_fb_sleep(void) +{ + int err = 0; + char buf; + int fd = open(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, O_RDONLY, 0); + // if the file doesn't exist, the error will be caught in read() below + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + ALOGE_IF(err < 0, + "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + close(fd); + return err < 0 ? err : 0; +} + +static void *earlysuspend_thread_func(void *arg) +{ + char buf[80]; + char wakeup_count[20]; + int wakeup_count_len; + int ret; + + while (1) { + if (wait_for_fb_sleep()) { + ALOGE("Failed reading wait_for_fb_sleep, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_MEM; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + + if (wait_for_fb_wake()) { + ALOGE("Failed reading wait_for_fb_wake, exiting earlysuspend thread\n"); + return NULL; + } + pthread_mutex_lock(&earlysuspend_mutex); + earlysuspend_state = EARLYSUSPEND_ON; + pthread_cond_signal(&earlysuspend_cond); + pthread_mutex_unlock(&earlysuspend_mutex); + } +} +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; + } + + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_MEM) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + + 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; + } + + if (wait_for_earlysuspend) { + pthread_mutex_lock(&earlysuspend_mutex); + while (earlysuspend_state != EARLYSUSPEND_ON) { + pthread_cond_wait(&earlysuspend_cond, &earlysuspend_mutex); + } + pthread_mutex_unlock(&earlysuspend_mutex); + } + + 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, +}; + +void start_earlysuspend_thread(void) +{ + char buf[80]; + int ret; + + ret = access(EARLYSUSPEND_WAIT_FOR_FB_SLEEP, F_OK); + if (ret < 0) { + return; + } + + ret = access(EARLYSUSPEND_WAIT_FOR_FB_WAKE, F_OK); + if (ret < 0) { + return; + } + + wait_for_fb_wake(); + + ALOGI("Starting early suspend unblocker thread\n"); + ret = pthread_create(&earlysuspend_thread, NULL, earlysuspend_thread_func, NULL); + if (ret) { + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error creating thread: %s\n", buf); + return; + } + + wait_for_earlysuspend = true; +} + +struct autosuspend_ops *autosuspend_earlysuspend_init(void) +{ + char buf[80]; + int ret; + + sPowerStatefd = open(EARLYSUSPEND_SYS_POWER_STATE, O_RDWR); + + if (sPowerStatefd < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGW("Error opening %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + return NULL; + } + + ret = write(sPowerStatefd, "on", 2); + if (ret < 0) { + strerror_r(errno, buf, sizeof(buf)); + ALOGW("Error writing 'on' to %s: %s\n", EARLYSUSPEND_SYS_POWER_STATE, buf); + goto err_write; + } + + ALOGI("Selected early suspend\n"); + + start_earlysuspend_thread(); + + return &autosuspend_earlysuspend_ops; + +err_write: + close(sPowerStatefd); + return NULL; +} 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..a88e677 --- /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 __attribute__((unused))) +{ + 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..4892866 --- /dev/null +++ b/libsync/sync.c @@ -0,0 +1,116 @@ +/* + * 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 <linux/sync.h> +#include <linux/sw_sync.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +int sync_wait(int fd, int timeout) +{ + __s32 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..386747a --- /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 __attribute__((unused)), char *argv[] __attribute__((unused))) +{ + 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..02a401d 100644 --- a/libsysutils/src/FrameworkListener.cpp +++ b/libsysutils/src/FrameworkListener.cpp @@ -25,13 +25,27 @@ #include <sysutils/FrameworkCommand.h> #include <sysutils/SocketClient.h> +static const int CMD_BUF_SIZE = 1024; + +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) { - char buffer[255]; + char buffer[CMD_BUF_SIZE]; int len; len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer))); @@ -40,6 +54,8 @@ bool FrameworkListener::onDataAvailable(SocketClient *c) { return false; } else if (!len) return false; + if(buffer[len-1] != '\0') + SLOGW("String is not zero-terminated"); int offset = 0; int i; @@ -51,6 +67,7 @@ bool FrameworkListener::onDataAvailable(SocketClient *c) { offset = i + 1; } } + return true; } @@ -62,13 +79,14 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { FrameworkCommandCollection::iterator i; int argc = 0; char *argv[FrameworkListener::CMD_ARGS_MAX]; - char tmp[255]; + char tmp[CMD_BUF_SIZE]; char *p = data; char *q = tmp; char *qlimit = tmp + sizeof(tmp) - 1; bool esc = false; bool quote = false; int k; + bool haveCmdNum = !mWithSeq; memset(argv, 0, sizeof(argv)); memset(tmp, 0, sizeof(tmp)); @@ -115,9 +133,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 +169,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; @@ -150,7 +185,6 @@ void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { goto out; } } - cli->sendMsg(500, "Command not recognized", false); out: int j; @@ -159,6 +193,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 722dcb2..ae0e077 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,34 +51,93 @@ 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; } -int SocketClient::sendMsg(const char *msg) { - if (mSocket < 0) { - errno = EHOSTUNREACH; - return -1; +/** 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; + char *oldresult; + + if(result == NULL) { + SLOGW("malloc error (%s)", strerror(errno)); + return NULL; + } + + *(current++) = '"'; + while (arg < end) { + switch (*arg) { + case '\\': + case '"': + *(current++) = '\\'; // fallthrough + default: + *(current++) = *(arg++); + } } + *(current++) = '"'; + *(current++) = '\0'; + oldresult = result; // save pointer in case realloc fails + result = (char *)realloc(result, current-result); + return result ? result : oldresult; +} + +int SocketClient::sendMsg(const char *msg) { // Send the message including null character if (sendData(msg, strlen(msg) + 1) != 0) { SLOGW("Unable to send msg '%s'", msg); @@ -77,18 +146,31 @@ 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; + if (mSocket < 0) { + errno = EHOSTUNREACH; + return -1; + } + if (len == 0) { 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 +180,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 +188,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/libusbhost/Android.mk b/libusbhost/Android.mk index 52b4ead..9565cc5 100644 --- a/libusbhost/Android.mk +++ b/libusbhost/Android.mk @@ -44,3 +44,13 @@ LOCAL_CFLAGS := -g -DUSE_LIBLOG LOCAL_SHARED_LIBRARIES := libcutils include $(BUILD_SHARED_LIBRARY) + +# Static library for target +# ======================================================== + +include $(CLEAR_VARS) + +LOCAL_MODULE := libusbhost +LOCAL_SRC_FILES := usbhost.c + +include $(BUILD_STATIC_LIBRARY) diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index 5d261cd..167fa60 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -33,6 +33,7 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <stddef.h> #include <sys/ioctl.h> #include <sys/types.h> @@ -49,16 +50,24 @@ #include "usbhost/usbhost.h" -#define USB_FS_DIR "/dev/bus/usb" -#define USB_FS_ID_SCANNER "/dev/bus/usb/%d/%d" -#define USB_FS_ID_FORMAT "/dev/bus/usb/%03d/%03d" +#define DEV_DIR "/dev" +#define USB_FS_DIR DEV_DIR "/bus/usb" +#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d" +#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d" // From drivers/usb/core/devio.c // I don't know why this isn't in a kernel header #define MAX_USBFS_BUFFER_SIZE 16384 +#define MAX_USBFS_WD_COUNT 10 + struct usb_host_context { - int fd; + int fd; + usb_device_added_cb cb_added; + usb_device_removed_cb cb_removed; + void *data; + int wds[MAX_USBFS_WD_COUNT]; + int wdd; }; struct usb_device { @@ -77,39 +86,72 @@ static inline int badname(const char *name) return 0; } +static int find_existing_devices_bus(char *busname, + usb_device_added_cb added_cb, + void *client_data) +{ + char devname[32]; + DIR *devdir; + struct dirent *de; + int done = 0; + + devdir = opendir(busname); + if(devdir == 0) return 0; + + while ((de = readdir(devdir)) && !done) { + if(badname(de->d_name)) continue; + + snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name); + done = added_cb(devname, client_data); + } // end of devdir while + closedir(devdir); + + return done; +} + /* returns true if one of the callbacks indicates we are done */ static int find_existing_devices(usb_device_added_cb added_cb, - usb_device_removed_cb removed_cb, void *client_data) { - char busname[32], devname[32]; - DIR *busdir , *devdir ; + char busname[32]; + DIR *busdir; struct dirent *de; int done = 0; busdir = opendir(USB_FS_DIR); - if(busdir == 0) return 1; + if(busdir == 0) return 0; while ((de = readdir(busdir)) != 0 && !done) { if(badname(de->d_name)) continue; - snprintf(busname, sizeof busname, "%s/%s", USB_FS_DIR, de->d_name); - devdir = opendir(busname); - if(devdir == 0) continue; - - while ((de = readdir(devdir)) && !done) { - if(badname(de->d_name)) continue; - - snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name); - done = added_cb(devname, client_data); - } // end of devdir while - closedir(devdir); + snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name); + done = find_existing_devices_bus(busname, added_cb, + client_data); } //end of busdir while closedir(busdir); return done; } +static void watch_existing_subdirs(struct usb_host_context *context, + int *wds, int wd_count) +{ + char path[100]; + int i, ret; + + wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); + if (wds[0] < 0) + return; + + /* watch existing subdirectories of USB_FS_DIR */ + for (i = 1; i < wd_count; i++) { + snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i); + ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); + if (ret >= 0) + wds[i] = ret; + } +} + struct usb_host_context *usb_host_init() { struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context)); @@ -132,76 +174,126 @@ void usb_host_cleanup(struct usb_host_context *context) free(context); } -void usb_host_run(struct usb_host_context *context, +int usb_host_get_fd(struct usb_host_context *context) +{ + return context->fd; +} /* usb_host_get_fd() */ + +int usb_host_load(struct usb_host_context *context, usb_device_added_cb added_cb, usb_device_removed_cb removed_cb, usb_discovery_done_cb discovery_done_cb, void *client_data) { - struct inotify_event* event; - char event_buf[512]; - char path[100]; - int i, ret, done = 0; - int wd, wds[10]; - int wd_count = sizeof(wds) / sizeof(wds[0]); + int done = 0; + int i; + + context->cb_added = added_cb; + context->cb_removed = removed_cb; + context->data = client_data; D("Created device discovery thread\n"); /* watch for files added and deleted within USB_FS_DIR */ - memset(wds, 0, sizeof(wds)); + for (i = 0; i < MAX_USBFS_WD_COUNT; i++) + context->wds[i] = -1; + /* watch the root for new subdirectories */ - wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); - if (wds[0] < 0) { + context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE); + if (context->wdd < 0) { fprintf(stderr, "inotify_add_watch failed\n"); if (discovery_done_cb) discovery_done_cb(client_data); - return; + return done; } - /* watch existing subdirectories of USB_FS_DIR */ - for (i = 1; i < wd_count; i++) { - snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i); - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; - } + watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); /* check for existing devices first, after we have inotify set up */ - done = find_existing_devices(added_cb, removed_cb, client_data); + done = find_existing_devices(added_cb, client_data); if (discovery_done_cb) done |= discovery_done_cb(client_data); - while (!done) { - ret = read(context->fd, event_buf, sizeof(event_buf)); - if (ret >= (int)sizeof(struct inotify_event)) { - event = (struct inotify_event *)event_buf; - wd = event->wd; - if (wd == wds[0]) { - i = atoi(event->name); - snprintf(path, sizeof(path), "%s/%s", USB_FS_DIR, event->name); - D("new subdirectory %s: index: %d\n", path, i); - if (i > 0 && i < wd_count) { - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; + return done; +} /* usb_host_load() */ + +int usb_host_read_event(struct usb_host_context *context) +{ + struct inotify_event* event; + char event_buf[512]; + char path[100]; + int i, ret, done = 0; + int j, event_size; + int wd; + + ret = read(context->fd, event_buf, sizeof(event_buf)); + if (ret >= (int)sizeof(struct inotify_event)) { + event = (struct inotify_event *)event_buf; + wd = event->wd; + if (wd == context->wdd) { + if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) { + watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); + done = find_existing_devices(context->cb_added, context->data); + } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "bus")) { + for (i = 0; i < MAX_USBFS_WD_COUNT; i++) { + if (context->wds[i] >= 0) { + inotify_rm_watch(context->fd, context->wds[i]); + context->wds[i] = -1; + } + } + } + } else if (wd == context->wds[0]) { + i = atoi(event->name); + snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name); + D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ? + "new" : "gone", path, i); + if (i > 0 && i < MAX_USBFS_WD_COUNT) { + if (event->mask & IN_CREATE) { + ret = inotify_add_watch(context->fd, path, + IN_CREATE | IN_DELETE); + if (ret >= 0) + context->wds[i] = ret; + done = find_existing_devices_bus(path, context->cb_added, + context->data); + } else if (event->mask & IN_DELETE) { + inotify_rm_watch(context->fd, context->wds[i]); + context->wds[i] = -1; } - } else { - for (i = 1; i < wd_count && !done; i++) { - if (wd == wds[i]) { - snprintf(path, sizeof(path), "%s/%03d/%s", USB_FS_DIR, i, event->name); - if (event->mask == IN_CREATE) { - D("new device %s\n", path); - done = added_cb(path, client_data); - } else if (event->mask == IN_DELETE) { - D("gone device %s\n", path); - done = removed_cb(path, client_data); - } + } + } else { + for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) { + if (wd == context->wds[i]) { + snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name); + if (event->mask == IN_CREATE) { + D("new device %s\n", path); + done = context->cb_added(path, context->data); + } else if (event->mask == IN_DELETE) { + D("gone device %s\n", path); + done = context->cb_removed(path, context->data); } } } } } -} + + return done; +} /* usb_host_read_event() */ + +void usb_host_run(struct usb_host_context *context, + usb_device_added_cb added_cb, + usb_device_removed_cb removed_cb, + usb_discovery_done_cb discovery_done_cb, + void *client_data) +{ + int done; + + done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data); + + while (!done) { + + done = usb_host_read_event(context); + } +} /* usb_host_run() */ struct usb_device *usb_device_open(const char *dev_name) { @@ -555,7 +647,6 @@ struct usb_request *usb_request_wait(struct usb_device *dev) { struct usbdevfs_urb *urb = NULL; struct usb_request *req = NULL; - int res; while (1) { int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb); diff --git a/libzipfile/centraldir.c b/libzipfile/centraldir.c index 0e264a3..911e2b9 100644 --- a/libzipfile/centraldir.c +++ b/libzipfile/centraldir.c @@ -192,7 +192,7 @@ read_central_dir(Zipfile *file) // too small to be a ZIP archive?
if (bufsize < EOCD_LEN) {
- fprintf(stderr, "Length is %d -- too small\n", bufsize);
+ fprintf(stderr, "Length is %zd -- too small\n", bufsize);
goto bail;
}
diff --git a/libzipfile/test_zipfile.c b/libzipfile/test_zipfile.c index 40840ec..1aaa913 100644 --- a/libzipfile/test_zipfile.c +++ b/libzipfile/test_zipfile.c @@ -1,5 +1,5 @@ #include <zipfile/zipfile.h>
-
+#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -55,6 +55,8 @@ main(int argc, char** argv) switch (what)
{
+ case HUH:
+ break;
case LIST:
dump_zipfile(stdout, zip);
break;
diff --git a/logcat/event.logtags b/logcat/event.logtags index 2e814ff..6040bd9 100644 --- a/logcat/event.logtags +++ b/logcat/event.logtags @@ -134,5 +134,24 @@ 70200 aggregation (aggregation time|2|3) 70201 aggregation_test (field1|1|2),(field2|1|2),(field3|1|2),(field4|1|2),(field5|1|2) +# libc failure logging +80100 bionic_event_memcpy_buffer_overflow (uid|1) +80105 bionic_event_strcat_buffer_overflow (uid|1) +80110 bionic_event_memmov_buffer_overflow (uid|1) +80115 bionic_event_strncat_buffer_overflow (uid|1) +80120 bionic_event_strncpy_buffer_overflow (uid|1) +80125 bionic_event_memset_buffer_overflow (uid|1) +80130 bionic_event_strcpy_buffer_overflow (uid|1) + +80200 bionic_event_strcat_integer_overflow (uid|1) +80205 bionic_event_strncat_integer_overflow (uid|1) + +80300 bionic_event_resolver_old_response (uid|1) +80305 bionic_event_resolver_wrong_server (uid|1) +80310 bionic_event_resolver_wrong_query (uid|1) + +# libcore failure logging +90100 cert_pin_failure (certs|4) + # NOTE - the range 1000000-2000000 is reserved for partners and others who # want to define their own log tags without conflicting with the core platform. diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index ae56c41..d3b5ed0 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -101,7 +101,7 @@ static EventTagMap* g_eventTagMap = NULL; static int openLogFile (const char *pathname) { - return open(g_outputFileName, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); + return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); } static void rotateLogs() @@ -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/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index a94cb9c..34a879b 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -72,7 +72,7 @@ int usage(void) -static unsigned char padding[4096] = { 0, }; +static unsigned char padding[16384] = { 0, }; int write_padding(int fd, unsigned pagesize, unsigned itemsize) { @@ -109,18 +109,17 @@ int main(int argc, char **argv) int fd; SHA_CTX ctx; uint8_t* sha; + unsigned base = 0x10000000; + unsigned kernel_offset = 0x00008000; + unsigned ramdisk_offset = 0x01000000; + unsigned second_offset = 0x00f00000; + unsigned tags_offset = 0x00000100; argc--; argv++; memset(&hdr, 0, sizeof(hdr)); - /* default load addresses */ - hdr.kernel_addr = 0x10008000; - hdr.ramdisk_addr = 0x11000000; - hdr.second_addr = 0x10F00000; - hdr.tags_addr = 0x10000100; - while(argc > 0){ char *arg = argv[0]; char *val = argv[1]; @@ -140,16 +139,21 @@ int main(int argc, char **argv) } else if(!strcmp(arg, "--cmdline")) { cmdline = val; } else if(!strcmp(arg, "--base")) { - unsigned base = strtoul(val, 0, 16); - hdr.kernel_addr = base + 0x00008000; - hdr.ramdisk_addr = base + 0x01000000; - hdr.second_addr = base + 0x00F00000; - hdr.tags_addr = base + 0x00000100; + base = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--kernel_offset")) { + kernel_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--ramdisk_offset")) { + ramdisk_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--second_offset")) { + second_offset = strtoul(val, 0, 16); + } else if(!strcmp(arg, "--tags_offset")) { + tags_offset = strtoul(val, 0, 16); } else if(!strcmp(arg, "--board")) { board = val; } else if(!strcmp(arg,"--pagesize")) { pagesize = strtoul(val, 0, 10); - if ((pagesize != 2048) && (pagesize != 4096)) { + if ((pagesize != 2048) && (pagesize != 4096) + && (pagesize != 8192) && (pagesize != 16384)) { fprintf(stderr,"error: unsupported page size %d\n", pagesize); return -1; } @@ -159,6 +163,10 @@ int main(int argc, char **argv) } hdr.page_size = pagesize; + hdr.kernel_addr = base + kernel_offset; + hdr.ramdisk_addr = base + ramdisk_offset; + hdr.second_addr = base + second_offset; + hdr.tags_addr = base + tags_offset; if(bootimg == 0) { fprintf(stderr,"error: no output filename specified\n"); 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/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/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..64ff522 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -15,6 +15,10 @@ ifeq ($(TARGET_PRODUCT),full_x86) copy_from += etc/vold.fstab endif +ifeq ($(TARGET_PRODUCT),full_mips) +copy_from += etc/vold.fstab +endif + # the /system/etc/init.goldfish.sh is needed to enable emulator support # in the system image. In theory, we don't need these for -user builds # which are device-specific. However, these builds require at the moment @@ -50,6 +54,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..1373be8 100644 --- a/rootdir/etc/init.goldfish.rc +++ b/rootdir/etc/init.goldfish.rc @@ -3,8 +3,13 @@ on early-init mkdir /mnt/sdcard 0000 system system # for backwards compatibility symlink /mnt/sdcard /sdcard + mount debugfs debugfs /sys/kernel/debug 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 1e3ed8c..5206cd1 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.usb.rc import /init.${ro.hardware}.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 @@ -22,9 +34,10 @@ loglevel 3 export ANDROID_ROOT /system export ANDROID_ASSETS /system/app export ANDROID_DATA /data + export ANDROID_STORAGE /storage 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/conscrypt.jar:/system/framework/okhttp.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 @@ -44,8 +57,14 @@ loglevel 3 mkdir /cache 0770 system cache mkdir /config 0500 root root + # See storage config details at http://source.android.com/tech/storage/ + mkdir /mnt/shell 0700 shell shell + mkdir /storage 0050 root sdcard_r + # Directory for putting things only root should see. mkdir /mnt/secure 0700 root root + # Create private mountpoint so we can MS_MOVE from staging + mount tmpfs tmpfs /mnt/secure mode=0700,uid=0,gid=0 # Directory for staging bindmounts mkdir /mnt/secure/staging 0700 root root @@ -70,25 +89,43 @@ 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/net/ipv4/ping_group_range "0 2147483647" + 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 + +# qtaguid will limit access to specific data based on group memberships. +# net_bw_acct grants impersonation of socket owners. +# net_bw_stats grants access to other apps' detailed tagged-socket stats. + chown root net_bw_acct /proc/net/xt_qtaguid/ctrl + chown root net_bw_stats /proc/net/xt_qtaguid/stats # Allow everybody to read the xt_qtaguid resource tracking misc dev. # This is needed by any process that uses socket tagging. @@ -105,24 +142,36 @@ on fs on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount + # mount shared so changes propagate into child namespaces + mount rootfs rootfs / shared rec + mount tmpfs tmpfs /mnt/secure private rec # 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 chmod 0440 /proc/vmallocinfo + chown root log /proc/slabinfo + chmod 0440 /proc/slabinfo + #change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks chown root system /proc/kmsg chmod 0440 /proc/kmsg chown root system /proc/sysrq-trigger chmod 0220 /proc/sysrq-trigger + chown system log /proc/last_kmsg + chmod 0440 /proc/last_kmsg # create the lost+found directories, so as to enforce our permissions mkdir /cache/lost+found 0770 root root @@ -131,6 +180,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 @@ -150,21 +201,31 @@ on post-fs-data # create basic filesystem structure mkdir /data/misc 01771 system misc - mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth + mkdir /data/misc/adb 02750 system shell + mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack mkdir /data/misc/bluetooth 0770 system system mkdir /data/misc/keystore 0700 keystore keystore mkdir /data/misc/keychain 0771 system system + mkdir /data/misc/sms 0770 system radio + mkdir /data/misc/zoneinfo 0775 system system mkdir /data/misc/vpn 0770 system vpn mkdir /data/misc/systemkeys 0700 system system # give system access to wpa_supplicant.conf for backup and restore mkdir /data/misc/wifi 0770 wifi wifi chmod 0660 /data/misc/wifi/wpa_supplicant.conf - mkdir /data/local 0771 shell shell + 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-lib 0771 system system 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 @@ -177,8 +238,12 @@ on post-fs-data # create the lost+found directories, so as to enforce our permissions mkdir /data/lost+found 0770 root root - # create directory for DRM plug-ins - mkdir /data/drm 0774 drm drm + # create directory for DRM plug-ins - give drm the read/write access to + # the following directory. + mkdir /data/drm 0770 drm drm + + # Separate location for storing security policy files on data + mkdir /data/security 0711 system system # If there is no fs-post-data action in the init.<device>.rc file, you # must uncomment this line, otherwise encrypted filesystems @@ -186,11 +251,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 @@ -202,7 +262,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 @@ -220,12 +280,37 @@ 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/autosleep + 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 + chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration + chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration + + # 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 @@ -237,12 +322,6 @@ on boot chown system system /sys/class/leds/red/device/grpfreq chown system system /sys/class/leds/red/device/grppwm chown system system /sys/class/leds/red/device/blink - chown system system /sys/class/leds/red/brightness - chown system system /sys/class/leds/green/brightness - chown system system /sys/class/leds/blue/brightness - chown system system /sys/class/leds/red/device/grpfreq - chown system system /sys/class/leds/red/device/grppwm - chown system system /sys/class/leds/red/device/blink chown system system /sys/class/timed_output/vibrator/enable chown system system /sys/module/sco/parameters/disable_esco chown system system /sys/kernel/ipv4/tcp_wmem_min @@ -260,8 +339,12 @@ on boot setprop net.tcp.buffersize.lte 524288,1048576,2097152,262144,524288,1048576 setprop net.tcp.buffersize.umts 4094,87380,110208,4096,16384,110208 setprop net.tcp.buffersize.hspa 4094,87380,262144,4096,16384,262144 + setprop net.tcp.buffersize.hsupa 4094,87380,262144,4096,16384,262144 + setprop net.tcp.buffersize.hsdpa 4094,87380,262144,4096,16384,262144 + setprop net.tcp.buffersize.hspap 4094,87380,1220608,4096,16384,1220608 setprop net.tcp.buffersize.edge 4093,26280,35040,4096,16384,35040 setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 + setprop net.tcp.buffersize.evdo 4094,87380,262144,4096,16384,262144 # Set this property so surfaceflinger is not started by system_init setprop system_init.startsurfaceflinger 0 @@ -295,54 +378,16 @@ 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 + +on property:selinux.reload_policy=1 + restart ueventd + restart installd service console /system/bin/sh class core @@ -357,24 +402,14 @@ on property:ro.debuggable=1 # adbd is controlled via property triggers in init.<platform>.usb.rc service adbd /sbin/adbd class core + socket adbd stream 660 system system 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 @@ -394,6 +429,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 @@ -403,17 +439,17 @@ service ril-daemon /system/bin/rild socket rild stream 660 root radio socket rild-debug stream 660 radio system user root - group radio cache inet misc audio sdcard_rw log + group radio cache inet misc audio log service surfaceflinger /system/bin/surfaceflinger class main user system - group graphics + group graphics drmrpc onrestart restart zygote service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main - socket zygote stream 666 + socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media @@ -422,7 +458,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 service media /system/bin/mediaserver class main @@ -437,21 +473,6 @@ service bootanim /system/bin/bootanimation disabled oneshot -service dbus /system/bin/dbus-daemon --system --nofork - class main - socket dbus stream 660 bluetooth bluetooth - user bluetooth - group bluetooth net_bt_admin - -service bluetoothd /system/bin/bluetoothd -n - class main - socket bluetooth stream 660 bluetooth bluetooth - socket dbus_bluetooth stream 660 bluetooth bluetooth - # init.rc does not yet support applying capabilities, so run as root and - # let bluetoothd drop uid to bluetooth with the right linux capabilities - group bluetooth net_bt_admin misc - disabled - service installd /system/bin/installd class main socket installd stream 600 system system @@ -479,11 +500,22 @@ service mtpd /system/bin/mtpd service keystore /system/bin/keystore /data/misc/keystore class main user keystore - group keystore - socket keystore stream 666 + group keystore drmrpc service dumpstate /system/bin/dumpstate -s class main 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..8a05fd0 --- /dev/null +++ b/rootdir/init.trace.rc @@ -0,0 +1,33 @@ +## 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/power/clock_set_rate/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/power/clock_set_rate/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..f37b630 --- /dev/null +++ b/rootdir/init.usb.rc @@ -0,0 +1,92 @@ +# 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 none + setprop sys.usb.config ${persist.sys.usb.config} diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index 438cf0a..2cf0265 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 @@ -17,13 +18,17 @@ # gpu driver for adreno200 is globally accessible /dev/kgsl 0666 root root +# kms driver for drm based gpu +/dev/dri/* 0666 root graphics + # these should not be world writable /dev/diag 0660 radio radio /dev/diag_arm9 0660 radio radio /dev/android_adb 0660 adb adb /dev/android_adb_enable 0660 adb adb /dev/ttyMSM0 0600 bluetooth bluetooth -/dev/uinput 0660 system bluetooth +/dev/uhid 0660 system net_bt_stack +/dev/uinput 0660 system net_bt_stack /dev/alarm 0664 system radio /dev/tty0 0660 root system /dev/graphics/* 0660 root graphics diff --git a/run-as/Android.mk b/run-as/Android.mk index 326f5af..a8f2885 100644 --- a/run-as/Android.mk +++ b/run-as/Android.mk @@ -3,10 +3,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= run-as.c package.c -LOCAL_MODULE:= run-as - -LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SHARED_LIBRARIES := libselinux -LOCAL_STATIC_LIBRARIES := libc +LOCAL_MODULE:= run-as include $(BUILD_EXECUTABLE) diff --git a/run-as/package.c b/run-as/package.c index ca08436..27fc1eb 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,21 +44,21 @@ /* 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. + * Returns a pointer into the src string, leaving off where the copy + * has stopped. The copy will stop when dstlen, srclen or a null + * character on src has been reached. */ -static void +static const char* string_copy(char* dst, size_t dstlen, const char* src, size_t srclen) { const char* srcend = src + srclen; const char* dstend = dst + dstlen; if (dstlen == 0) - return; + return src; dstend--; /* make room for terminating zero */ @@ -65,42 +66,93 @@ string_copy(char* dst, size_t dstlen, const char* src, size_t srclen) *dst++ = *src++; *dst = '\0'; /* zero-terminate result */ + return src; } -/* 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; + gid_t oldegid; - /* check the input buffer size */ - if (buffsize >= INT_MAX) { - errno = EINVAL; - return -1; + *filesize = 0; + + /* + * Temporarily switch effective GID to allow us to read + * the packages file + */ + + oldegid = getegid(); + if (setegid(AID_SYSTEM) < 0) { + return NULL; } /* 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 NULL; + } - if (fd < 0) - return -1; + /* restore back to our old egid */ + if (setegid(oldegid) < 0) { + goto EXIT; + } - /* 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 +423,19 @@ 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'; + info->seinfo[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; @@ -390,13 +443,14 @@ get_package_info(const char* pkgName, PackageInfo *info) /* expect the following format on each line of the control file: * - * <pkgName> <uid> <debugFlag> <dataDir> + * <pkgName> <uid> <debugFlag> <dataDir> <seinfo> * * where: * <pkgName> is the package's name * <uid> is the application-specific user Id (decimal) * <debugFlag> is 1 if the package is debuggable, or 0 otherwise * <dataDir> is the path to the package's data directory (e.g. /data/data/com.example.foo) + * <seinfo> is the seinfo label associated with the package * * The file is generated in com.android.server.PackageManagerService.Settings.writeLP() */ @@ -452,10 +506,22 @@ get_package_info(const char* pkgName, PackageInfo *info) if (q == p) goto BAD_FORMAT; - string_copy(info->dataDir, sizeof info->dataDir, p, q - p); + p = string_copy(info->dataDir, sizeof info->dataDir, p, q - p); + + /* skip spaces */ + if (parse_spaces(&p, end) < 0) + goto BAD_FORMAT; + + /* fifth field is the seinfo string */ + q = skip_non_spaces(p, end); + if (q == p) + goto BAD_FORMAT; + + string_copy(info->seinfo, sizeof info->seinfo, p, q - p); /* Ignore the rest */ - return 0; + result = 0; + goto EXIT; NEXT_LINE: p = next; @@ -463,9 +529,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/package.h b/run-as/package.h index 852af06..34603c0 100644 --- a/run-as/package.h +++ b/run-as/package.h @@ -30,6 +30,7 @@ typedef struct { uid_t uid; char isDebuggable; char dataDir[PATH_MAX]; + char seinfo[PATH_MAX]; } PackageInfo; /* see documentation in package.c for these functiosn */ diff --git a/run-as/run-as.c b/run-as/run-as.c index d2a44e1..3c0ecc4 100644 --- a/run-as/run-as.c +++ b/run-as/run-as.c @@ -29,6 +29,7 @@ #include <time.h> #include <stdarg.h> +#include <selinux/android.h> #include <private/android_filesystem_config.h> #include "package.h" @@ -41,9 +42,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 @@ -162,6 +163,11 @@ int main(int argc, char **argv) return 1; } + if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) { + panic("Could not set SELinux security context: %s\n", strerror(errno)); + return 1; + } + /* User specified command for exec. */ if (argc >= 3 ) { if (execvp(argv[2], argv+2) < 0) { diff --git a/sdcard/Android.mk b/sdcard/Android.mk index c430ac8..fb04d6d 100644 --- a/sdcard/Android.mk +++ b/sdcard/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= sdcard.c LOCAL_MODULE:= sdcard +LOCAL_CFLAGS := -Wall -Wno-unused-parameter LOCAL_SHARED_LIBRARIES := libc diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 689cd2a..8d87ee9 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -25,7 +25,9 @@ #include <sys/statfs.h> #include <sys/uio.h> #include <dirent.h> +#include <limits.h> #include <ctype.h> +#include <pthread.h> #include <private/android_filesystem_config.h> @@ -40,12 +42,10 @@ * permissions at creation, owner, group, and permissions are not * changeable, symlinks and hardlinks are not createable, etc. * - * usage: sdcard <path> <uid> <gid> - * - * It must be run as root, but will change to uid/gid as soon as it - * mounts a filesystem on /mnt/sdcard. It will refuse to run if uid or - * gid are zero. + * See usage() for command line options. * + * It must be run as root, but will drop to requested UID/GID as soon as it + * mounts a filesystem. It will refuse to run if requested UID/GID are zero. * * Things I believe to be true: * @@ -55,7 +55,6 @@ * - if an op that returns a fuse_entry fails writing the reply to the * kernel, you must rollback the refcount to reflect the reference the * kernel did not actually acquire - * */ #define FUSE_TRACE 0 @@ -70,30 +69,42 @@ #define FUSE_UNKNOWN_INO 0xffffffff -#define MOUNT_POINT "/mnt/sdcard" +/* Maximum number of bytes to write in one request. */ +#define MAX_WRITE (256 * 1024) + +/* Maximum number of bytes to read in one request. */ +#define MAX_READ (128 * 1024) + +/* Largest possible request. + * The request size is bounded by the maximum size of a FUSE_WRITE request because it has + * the largest possible data payload. */ +#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE) + +/* Default number of threads. */ +#define DEFAULT_NUM_THREADS 2 + +/* Pseudo-error constant used to indicate that no fuse status is needed + * or that a reply has already been written. */ +#define NO_STATUS 1 struct handle { - struct node *node; int fd; }; struct dirhandle { - struct node *node; DIR *d; }; struct node { + __u32 refcount; __u64 nid; __u64 gen; struct node *next; /* per-dir sibling list */ struct node *child; /* first contained file by this dir */ - struct node *all; /* global node list */ struct node *parent; /* containing directory */ - __u32 refcount; - __u32 namelen; - + size_t namelen; char *name; /* If non-null, this is the real name of the file in the underlying storage. * This may differ from the field "name" only by case. @@ -103,98 +114,166 @@ struct node { char *actual_name; }; +/* Global data structure shared by all fuse handlers. */ struct fuse { - __u64 next_generation; - __u64 next_node_id; + pthread_mutex_t lock; + __u64 next_generation; int fd; - - struct node *all; - struct node root; - char rootpath[1024]; + char rootpath[PATH_MAX]; }; -static unsigned uid = -1; -static unsigned gid = -1; +/* Private data used by a single fuse handler. */ +struct fuse_handler { + struct fuse* fuse; + int token; + + /* To save memory, we never use the contents of the request buffer and the read + * buffer at the same time. This allows us to share the underlying storage. */ + union { + __u8 request_buffer[MAX_REQUEST_SIZE]; + __u8 read_buffer[MAX_READ]; + }; +}; -#define PATH_BUFFER_SIZE 1024 +static inline void *id_to_ptr(__u64 nid) +{ + return (void *) (uintptr_t) nid; +} -#define NO_CASE_SENSITIVE_MATCH 0 -#define CASE_SENSITIVE_MATCH 1 +static inline __u64 ptr_to_id(void *ptr) +{ + return (__u64) (uintptr_t) ptr; +} -/* - * Get the real-life absolute path to a node. - * node: start at this node - * buf: storage for returned string - * name: append this string to path if set +static void acquire_node_locked(struct node* node) +{ + node->refcount++; + TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); +} + +static void remove_node_from_parent_locked(struct node* node); + +static void release_node_locked(struct node* node) +{ + TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); + if (node->refcount > 0) { + node->refcount--; + if (!node->refcount) { + TRACE("DESTROY %p (%s)\n", node, node->name); + remove_node_from_parent_locked(node); + + /* TODO: remove debugging - poison memory */ + memset(node->name, 0xef, node->namelen); + free(node->name); + free(node->actual_name); + memset(node, 0xfc, sizeof(*node)); + free(node); + } + } else { + ERROR("Zero refcnt %p\n", node); + } +} + +static void add_node_to_parent_locked(struct node *node, struct node *parent) { + node->parent = parent; + node->next = parent->child; + parent->child = node; + acquire_node_locked(parent); +} + +static void remove_node_from_parent_locked(struct node* node) +{ + if (node->parent) { + if (node->parent->child == node) { + node->parent->child = node->parent->child->next; + } else { + struct node *node2; + node2 = node->parent->child; + while (node2->next != node) + node2 = node2->next; + node2->next = node->next; + } + release_node_locked(node->parent); + node->parent = NULL; + node->next = NULL; + } +} + +/* Gets the absolute path to a node into the provided buffer. + * + * Populates 'buf' with the path and returns the length of the path on success, + * or returns -1 if the path is too long for the provided buffer. */ -char *do_node_get_path(struct node *node, char *buf, const char *name, int match_case_insensitive) +static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) { - struct node *in_node = node; - const char *in_name = name; - char *out = buf + PATH_BUFFER_SIZE - 1; - int len; - out[0] = 0; - - if (name) { - len = strlen(name); - goto start; - } - - while (node) { - name = (node->actual_name ? node->actual_name : node->name); - len = node->namelen; - node = node->parent; - start: - if ((len + 1) > (out - buf)) - return 0; - out -= len; - memcpy(out, name, len); - /* avoid double slash at beginning of path */ - if (out[0] != '/') { - out --; - out[0] = '/'; + size_t namelen = node->namelen; + if (bufsize < namelen + 1) { + return -1; + } + + ssize_t pathlen = 0; + if (node->parent) { + pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2); + if (pathlen < 0) { + return -1; } + buf[pathlen++] = '/'; } - /* If we are searching for a file within node (rather than computing node's path) - * and fail, then we need to look for a case insensitive match. - */ - if (in_name && match_case_insensitive && access(out, F_OK) != 0) { - char *path, buffer[PATH_BUFFER_SIZE]; - DIR* dir; + const char* name = node->actual_name ? node->actual_name : node->name; + memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */ + return pathlen + namelen; +} + +/* Finds the absolute path of a file within a given directory. + * Performs a case-insensitive search for the file and sets the buffer to the path + * of the first matching file. If 'search' is zero or if no match is found, sets + * the buffer to the path that the file would have, assuming the name were case-sensitive. + * + * Populates 'buf' with the path and returns the actual name (within 'buf') on success, + * or returns NULL if the path is too long for the provided buffer. + */ +static char* find_file_within(const char* path, const char* name, + char* buf, size_t bufsize, int search) +{ + size_t pathlen = strlen(path); + size_t namelen = strlen(name); + size_t childlen = pathlen + namelen + 1; + char* actual; + + if (bufsize <= childlen) { + return NULL; + } + + memcpy(buf, path, pathlen); + buf[pathlen] = '/'; + actual = buf + pathlen + 1; + memcpy(actual, name, namelen + 1); + + if (search && access(buf, F_OK)) { struct dirent* entry; - path = do_node_get_path(in_node, buffer, NULL, NO_CASE_SENSITIVE_MATCH); - dir = opendir(path); + DIR* dir = opendir(path); if (!dir) { ERROR("opendir %s failed: %s", path, strerror(errno)); - return out; + return actual; } - while ((entry = readdir(dir))) { - if (!strcasecmp(entry->d_name, in_name)) { - /* we have a match - replace the name */ - len = strlen(in_name); - memcpy(buf + PATH_BUFFER_SIZE - len - 1, entry->d_name, len); + if (!strcasecmp(entry->d_name, name)) { + /* we have a match - replace the name, don't need to copy the null again */ + memcpy(actual, entry->d_name, namelen); break; } } closedir(dir); } - - return out; -} - -char *node_get_path(struct node *node, char *buf, const char *name) -{ - /* We look for case insensitive matches by default */ - return do_node_get_path(node, buf, name, CASE_SENSITIVE_MATCH); + return actual; } -void attr_from_stat(struct fuse_attr *attr, struct stat *s) +static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, __u64 nid) { - attr->ino = s->st_ino; + attr->ino = nid; attr->size = s->st_size; attr->blocks = s->st_blocks; attr->atime = s->st_atime; @@ -221,806 +300,1059 @@ void attr_from_stat(struct fuse_attr *attr, struct stat *s) attr->gid = AID_SDCARD_RW; } -int node_get_attr(struct node *node, struct fuse_attr *attr) +struct node *create_node_locked(struct fuse* fuse, + struct node *parent, const char *name, const char* actual_name) { - int res; - struct stat s; - char *path, buffer[PATH_BUFFER_SIZE]; + struct node *node; + size_t namelen = strlen(name); - path = node_get_path(node, buffer, 0); - res = lstat(path, &s); - if (res < 0) { - ERROR("lstat('%s') errno %d\n", path, errno); - return -1; + node = calloc(1, sizeof(struct node)); + if (!node) { + return NULL; } - - attr_from_stat(attr, &s); - attr->ino = node->nid; - - return 0; -} - -static void add_node_to_parent(struct node *node, struct node *parent) { - node->parent = parent; - node->next = parent->child; - parent->child = node; - parent->refcount++; + node->name = malloc(namelen + 1); + if (!node->name) { + free(node); + return NULL; + } + memcpy(node->name, name, namelen + 1); + if (strcmp(name, actual_name)) { + node->actual_name = malloc(namelen + 1); + if (!node->actual_name) { + free(node->name); + free(node); + return NULL; + } + memcpy(node->actual_name, actual_name, namelen + 1); + } + node->namelen = namelen; + node->nid = ptr_to_id(node); + node->gen = fuse->next_generation++; + acquire_node_locked(node); + add_node_to_parent_locked(node, parent); + return node; } -/* Check to see if our parent directory already has a file with a name - * that differs only by case. If we find one, store it in the actual_name - * field so node_get_path will map it to this file in the underlying storage. - */ -static void node_find_actual_name(struct node *node) +static int rename_node_locked(struct node *node, const char *name, + const char* actual_name) { - char *path, buffer[PATH_BUFFER_SIZE]; - const char *node_name = node->name; - DIR* dir; - struct dirent* entry; - - if (!node->parent) return; - - path = node_get_path(node->parent, buffer, 0); - dir = opendir(path); - if (!dir) { - ERROR("opendir %s failed: %s", path, strerror(errno)); - return; + size_t namelen = strlen(name); + int need_actual_name = strcmp(name, actual_name); + + /* make the storage bigger without actually changing the name + * in case an error occurs part way */ + if (namelen > node->namelen) { + char* new_name = realloc(node->name, namelen + 1); + if (!new_name) { + return -ENOMEM; + } + node->name = new_name; + if (need_actual_name && node->actual_name) { + char* new_actual_name = realloc(node->actual_name, namelen + 1); + if (!new_actual_name) { + return -ENOMEM; + } + node->actual_name = new_actual_name; + } } - while ((entry = readdir(dir))) { - const char *test_name = entry->d_name; - if (strcmp(test_name, node_name) && !strcasecmp(test_name, node_name)) { - /* we have a match - differs but only by case */ - node->actual_name = strdup(test_name); + /* update the name, taking care to allocate storage before overwriting the old name */ + if (need_actual_name) { + if (!node->actual_name) { + node->actual_name = malloc(namelen + 1); if (!node->actual_name) { - ERROR("strdup failed - out of memory\n"); - exit(1); + return -ENOMEM; } - break; } + memcpy(node->actual_name, actual_name, namelen + 1); + } else { + free(node->actual_name); + node->actual_name = NULL; } - closedir(dir); + memcpy(node->name, name, namelen + 1); + node->namelen = namelen; + return 0; } -struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen) +static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid) { - struct node *node; - int namelen = strlen(name); - - node = calloc(1, sizeof(struct node)); - if (node == 0) { - return 0; - } - node->name = malloc(namelen + 1); - if (node->name == 0) { - free(node); - return 0; + if (nid == FUSE_ROOT_ID) { + return &fuse->root; + } else { + return id_to_ptr(nid); } +} - node->nid = nid; - node->gen = gen; - add_node_to_parent(node, parent); - memcpy(node->name, name, namelen + 1); - node->namelen = namelen; - node_find_actual_name(node); +static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid, + char* buf, size_t bufsize) +{ + struct node* node = lookup_node_by_id_locked(fuse, nid); + if (node && get_node_path_locked(node, buf, bufsize) < 0) { + node = NULL; + } return node; } -static char *rename_node(struct node *node, const char *name) +static struct node *lookup_child_by_name_locked(struct node *node, const char *name) { - node->namelen = strlen(name); - char *newname = realloc(node->name, node->namelen + 1); - if (newname == 0) - return 0; - node->name = newname; - memcpy(node->name, name, node->namelen + 1); - node_find_actual_name(node); - return node->name; + for (node = node->child; node; node = node->next) { + /* use exact string comparison, nodes that differ by case + * must be considered distinct even if they refer to the same + * underlying file as otherwise operations such as "mv x x" + * will not work because the source and target nodes are the same. */ + if (!strcmp(name, node->name)) { + return node; + } + } + return 0; +} + +static struct node* acquire_or_create_child_locked( + struct fuse* fuse, struct node* parent, + const char* name, const char* actual_name) +{ + struct node* child = lookup_child_by_name_locked(parent, name); + if (child) { + acquire_node_locked(child); + } else { + child = create_node_locked(fuse, parent, name, actual_name); + } + return child; } -void fuse_init(struct fuse *fuse, int fd, const char *path) +static void fuse_init(struct fuse *fuse, int fd, const char *source_path) { + pthread_mutex_init(&fuse->lock, NULL); + fuse->fd = fd; - fuse->next_node_id = 2; fuse->next_generation = 0; - fuse->all = &fuse->root; - memset(&fuse->root, 0, sizeof(fuse->root)); fuse->root.nid = FUSE_ROOT_ID; /* 1 */ fuse->root.refcount = 2; - rename_node(&fuse->root, path); + fuse->root.namelen = strlen(source_path); + fuse->root.name = strdup(source_path); } -static inline void *id_to_ptr(__u64 nid) +static void fuse_status(struct fuse *fuse, __u64 unique, int err) { - return (void *) nid; + struct fuse_out_header hdr; + hdr.len = sizeof(hdr); + hdr.error = err; + hdr.unique = unique; + write(fuse->fd, &hdr, sizeof(hdr)); } -static inline __u64 ptr_to_id(void *ptr) +static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) { - return (__u64) ptr; + struct fuse_out_header hdr; + struct iovec vec[2]; + int res; + + hdr.len = len + sizeof(hdr); + hdr.error = 0; + hdr.unique = unique; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = data; + vec[1].iov_len = len; + + res = writev(fuse->fd, vec, 2); + if (res < 0) { + ERROR("*** REPLY FAILED *** %d\n", errno); + } } +static int fuse_reply_entry(struct fuse* fuse, __u64 unique, + struct node* parent, const char* name, const char* actual_name, + const char* path) +{ + struct node* node; + struct fuse_entry_out out; + struct stat s; + + if (lstat(path, &s) < 0) { + return -errno; + } + + pthread_mutex_lock(&fuse->lock); + node = acquire_or_create_child_locked(fuse, parent, name, actual_name); + if (!node) { + pthread_mutex_unlock(&fuse->lock); + return -ENOMEM; + } + memset(&out, 0, sizeof(out)); + attr_from_stat(&out.attr, &s, node->nid); + out.attr_valid = 10; + out.entry_valid = 10; + out.nodeid = node->nid; + out.generation = node->gen; + pthread_mutex_unlock(&fuse->lock); + fuse_reply(fuse, unique, &out, sizeof(out)); + return NO_STATUS; +} -struct node *lookup_by_inode(struct fuse *fuse, __u64 nid) +static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid, + const char* path) { - if (nid == FUSE_ROOT_ID) { - return &fuse->root; - } else { - return id_to_ptr(nid); + struct fuse_attr_out out; + struct stat s; + + if (lstat(path, &s) < 0) { + return -errno; } + memset(&out, 0, sizeof(out)); + attr_from_stat(&out.attr, &s, nid); + out.attr_valid = 10; + fuse_reply(fuse, unique, &out, sizeof(out)); + return NO_STATUS; } -struct node *lookup_child_by_name(struct node *node, const char *name) +static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const char* name) { - for (node = node->child; node; node = node->next) { - if (!strcmp(name, node->name)) { - return node; - } + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] LOOKUP %s @ %llx (%s)\n", handler->token, name, hdr->nodeid, + parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; } - return 0; + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); } -struct node *lookup_child_by_inode(struct node *node, __u64 nid) +static int handle_forget(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_forget_in *req) { - for (node = node->child; node; node = node->next) { - if (node->nid == nid) { - return node; + struct node* node; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_by_id_locked(fuse, hdr->nodeid); + TRACE("[%d] FORGET #%lld @ %llx (%s)\n", handler->token, req->nlookup, + hdr->nodeid, node ? node->name : "?"); + if (node) { + __u64 n = req->nlookup; + while (n--) { + release_node_locked(node); } } - return 0; + pthread_mutex_unlock(&fuse->lock); + return NO_STATUS; /* no reply */ } -static void dec_refcount(struct node *node) { - if (node->refcount > 0) { - node->refcount--; - TRACE("dec_refcount %p(%s) -> %d\n", node, node->name, node->refcount); - } else { - ERROR("Zero refcnt %p\n", node); +static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_getattr_in *req) +{ + struct node* node; + char path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] GETATTR flags=%x fh=%llx @ %llx (%s)\n", handler->token, + req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!node) { + return -ENOENT; } - } + return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); +} -static struct node *remove_child(struct node *parent, __u64 nid) +static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const struct fuse_setattr_in *req) { - struct node *prev = 0; - struct node *node; + struct node* node; + char path[PATH_MAX]; + struct timespec times[2]; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token, + req->fh, req->valid, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); - for (node = parent->child; node; node = node->next) { - if (node->nid == nid) { - if (prev) { - prev->next = node->next; + if (!node) { + return -ENOENT; + } + + /* XXX: incomplete implementation on purpose. + * chmod/chown should NEVER be implemented.*/ + + if ((req->valid & FATTR_SIZE) && truncate(path, req->size) < 0) { + return -errno; + } + + /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW + * are both set, then set it to the current time. Else, set it to the + * time specified in the request. Same goes for mtime. Use utimensat(2) + * as it allows ATIME and MTIME to be changed independently, and has + * nanosecond resolution which fuse also has. + */ + if (req->valid & (FATTR_ATIME | FATTR_MTIME)) { + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_nsec = UTIME_OMIT; + if (req->valid & FATTR_ATIME) { + if (req->valid & FATTR_ATIME_NOW) { + times[0].tv_nsec = UTIME_NOW; } else { - parent->child = node->next; + times[0].tv_sec = req->atime; + times[0].tv_nsec = req->atimensec; } - node->next = 0; - node->parent = 0; - dec_refcount(parent); - return node; } - prev = node; + if (req->valid & FATTR_MTIME) { + if (req->valid & FATTR_MTIME_NOW) { + times[1].tv_nsec = UTIME_NOW; + } else { + times[1].tv_sec = req->mtime; + times[1].tv_nsec = req->mtimensec; + } + } + TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n", + handler->token, path, times[0].tv_sec, times[1].tv_sec); + if (utimensat(-1, path, times, 0) < 0) { + return -errno; + } + } + return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); +} + +static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token, + name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; + } + __u32 mode = (req->mode & (~0777)) | 0664; + if (mknod(child_path, mode, req->rdev) < 0) { + return -errno; + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); +} + +static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + const char* actual_name; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token, + name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !(actual_name = find_file_within(parent_path, name, + child_path, sizeof(child_path), 1))) { + return -ENOENT; + } + __u32 mode = (req->mode & (~0777)) | 0775; + if (mkdir(child_path, mode) < 0) { + return -errno; + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); +} + +static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token, + name, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !find_file_within(parent_path, name, + child_path, sizeof(child_path), 1)) { + return -ENOENT; + } + if (unlink(child_path) < 0) { + return -errno; + } + return 0; +} + +static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const char* name) +{ + struct node* parent_node; + char parent_path[PATH_MAX]; + char child_path[PATH_MAX]; + + pthread_mutex_lock(&fuse->lock); + parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + parent_path, sizeof(parent_path)); + TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token, + name, hdr->nodeid, parent_node ? parent_node->name : "?"); + pthread_mutex_unlock(&fuse->lock); + + if (!parent_node || !find_file_within(parent_path, name, + child_path, sizeof(child_path), 1)) { + return -ENOENT; + } + if (rmdir(child_path) < 0) { + return -errno; } return 0; } -struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name, - struct fuse_attr *attr) +static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_rename_in* req, + const char* old_name, const char* new_name) { + struct node* old_parent_node; + struct node* new_parent_node; + struct node* child_node; + char old_parent_path[PATH_MAX]; + char new_parent_path[PATH_MAX]; + char old_child_path[PATH_MAX]; + char new_child_path[PATH_MAX]; + const char* new_actual_name; int res; - struct stat s; - char *path, buffer[PATH_BUFFER_SIZE]; - struct node *node; - path = node_get_path(parent, buffer, name); - /* XXX error? */ + pthread_mutex_lock(&fuse->lock); + old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, + old_parent_path, sizeof(old_parent_path)); + new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir, + new_parent_path, sizeof(new_parent_path)); + TRACE("[%d] RENAME %s->%s @ %llx (%s) -> %llx (%s)\n", handler->token, + old_name, new_name, + hdr->nodeid, old_parent_node ? old_parent_node->name : "?", + req->newdir, new_parent_node ? new_parent_node->name : "?"); + if (!old_parent_node || !new_parent_node) { + res = -ENOENT; + goto lookup_error; + } + child_node = lookup_child_by_name_locked(old_parent_node, old_name); + if (!child_node || get_node_path_locked(child_node, + old_child_path, sizeof(old_child_path)) < 0) { + res = -ENOENT; + goto lookup_error; + } + acquire_node_locked(child_node); + pthread_mutex_unlock(&fuse->lock); - res = lstat(path, &s); - if (res < 0) - return 0; - - node = lookup_child_by_name(parent, name); - if (!node) { - node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++); - if (!node) - return 0; - node->nid = ptr_to_id(node); - node->all = fuse->all; - fuse->all = node; + /* Special case for renaming a file where destination is same path + * differing only by case. In this case we don't want to look for a case + * insensitive match. This allows commands like "mv foo FOO" to work as expected. + */ + int search = old_parent_node != new_parent_node + || strcasecmp(old_name, new_name); + if (!(new_actual_name = find_file_within(new_parent_path, new_name, + new_child_path, sizeof(new_child_path), search))) { + res = -ENOENT; + goto io_error; } - attr_from_stat(attr, &s); - attr->ino = node->nid; + TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path); + res = rename(old_child_path, new_child_path); + if (res < 0) { + res = -errno; + goto io_error; + } - return node; + pthread_mutex_lock(&fuse->lock); + res = rename_node_locked(child_node, new_name, new_actual_name); + if (!res) { + remove_node_from_parent_locked(child_node); + add_node_to_parent_locked(child_node, new_parent_node); + } + goto done; + +io_error: + pthread_mutex_lock(&fuse->lock); +done: + release_node_locked(child_node); +lookup_error: + pthread_mutex_unlock(&fuse->lock); + return res; } -void node_release(struct node *node) +static int handle_open(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_open_in* req) { - TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); - dec_refcount(node); - if (node->refcount == 0) { - if (node->parent->child == node) { - node->parent->child = node->parent->child->next; - } else { - struct node *node2; + struct node* node; + char path[PATH_MAX]; + struct fuse_open_out out; + struct handle *h; - node2 = node->parent->child; - while (node2->next != node) - node2 = node2->next; - node2->next = node->next; - } + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token, + req->flags, hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); - TRACE("DESTROY %p (%s)\n", node, node->name); + if (!node) { + return -ENOENT; + } + h = malloc(sizeof(*h)); + if (!h) { + return -ENOMEM; + } + TRACE("[%d] OPEN %s\n", handler->token, path); + h->fd = open(path, req->flags); + if (h->fd < 0) { + free(h); + return -errno; + } + out.fh = ptr_to_id(h); + out.open_flags = 0; + out.padding = 0; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} - node_release(node->parent); +static int handle_read(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_read_in* req) +{ + struct handle *h = id_to_ptr(req->fh); + __u64 unique = hdr->unique; + __u32 size = req->size; + __u64 offset = req->offset; + int res; - node->parent = 0; - node->next = 0; + /* Don't access any other fields of hdr or req beyond this point, the read buffer + * overlaps the request buffer and will clobber data in the request. This + * saves us 128KB per request handler thread at the cost of this scary comment. */ - /* TODO: remove debugging - poison memory */ - memset(node->name, 0xef, node->namelen); - free(node->name); - free(node->actual_name); - memset(node, 0xfc, sizeof(*node)); - free(node); + TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token, + h, h->fd, size, offset); + if (size > sizeof(handler->read_buffer)) { + return -EINVAL; + } + res = pread64(h->fd, handler->read_buffer, size, offset); + if (res < 0) { + return -errno; } + fuse_reply(fuse, unique, handler->read_buffer, res); + return NO_STATUS; } -void fuse_status(struct fuse *fuse, __u64 unique, int err) +static int handle_write(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_write_in* req, + const void* buffer) { - struct fuse_out_header hdr; - hdr.len = sizeof(hdr); - hdr.error = err; - hdr.unique = unique; - if (err) { -// ERROR("*** %d ***\n", err); + struct fuse_write_out out; + struct handle *h = id_to_ptr(req->fh); + int res; + + TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token, + h, h->fd, req->size, req->offset); + res = pwrite64(h->fd, buffer, req->size, req->offset); + if (res < 0) { + return -errno; } - write(fuse->fd, &hdr, sizeof(hdr)); + out.size = res; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; } -void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) +static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr) { - struct fuse_out_header hdr; - struct iovec vec[2]; + char path[PATH_MAX]; + struct statfs stat; + struct fuse_statfs_out out; int res; - hdr.len = len + sizeof(hdr); - hdr.error = 0; - hdr.unique = unique; + pthread_mutex_lock(&fuse->lock); + TRACE("[%d] STATFS\n", handler->token); + res = get_node_path_locked(&fuse->root, path, sizeof(path)); + pthread_mutex_unlock(&fuse->lock); + if (res < 0) { + return -ENOENT; + } + if (statfs(fuse->root.name, &stat) < 0) { + return -errno; + } + memset(&out, 0, sizeof(out)); + out.st.blocks = stat.f_blocks; + out.st.bfree = stat.f_bfree; + out.st.bavail = stat.f_bavail; + out.st.files = stat.f_files; + out.st.ffree = stat.f_ffree; + out.st.bsize = stat.f_bsize; + out.st.namelen = stat.f_namelen; + out.st.frsize = stat.f_frsize; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} - vec[0].iov_base = &hdr; - vec[0].iov_len = sizeof(hdr); - vec[1].iov_base = data; - vec[1].iov_len = len; +static int handle_release(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_release_in* req) +{ + struct handle *h = id_to_ptr(req->fh); - res = writev(fuse->fd, vec, 2); + TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd); + close(h->fd); + free(h); + return 0; +} + +static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_fsync_in* req) +{ + int is_data_sync = req->fsync_flags & 1; + struct handle *h = id_to_ptr(req->fh); + int res; + + TRACE("[%d] FSYNC %p(%d) is_data_sync=%d\n", handler->token, + h, h->fd, is_data_sync); + res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd); if (res < 0) { - ERROR("*** REPLY FAILED *** %d\n", errno); + return -errno; } + return 0; } -void lookup_entry(struct fuse *fuse, struct node *node, - const char *name, __u64 unique) +static int handle_flush(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr) { - struct fuse_entry_out out; - - memset(&out, 0, sizeof(out)); + TRACE("[%d] FLUSH\n", handler->token); + return 0; +} + +static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_open_in* req) +{ + struct node* node; + char path[PATH_MAX]; + struct fuse_open_out out; + struct dirhandle *h; + + pthread_mutex_lock(&fuse->lock); + node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); + TRACE("[%d] OPENDIR @ %llx (%s)\n", handler->token, + hdr->nodeid, node ? node->name : "?"); + pthread_mutex_unlock(&fuse->lock); - node = node_lookup(fuse, node, name, &out.attr); if (!node) { - fuse_status(fuse, unique, -ENOENT); - return; + return -ENOENT; } - - node->refcount++; -// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); - out.nodeid = node->nid; - out.generation = node->gen; - out.entry_valid = 10; - out.attr_valid = 10; - - fuse_reply(fuse, unique, &out, sizeof(out)); + h = malloc(sizeof(*h)); + if (!h) { + return -ENOMEM; + } + TRACE("[%d] OPENDIR %s\n", handler->token, path); + h->d = opendir(path); + if (!h->d) { + free(h); + return -errno; + } + out.fh = ptr_to_id(h); + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; } -void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len) +static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_read_in* req) { - struct node *node; - - if ((len < sizeof(*hdr)) || (hdr->len != len)) { - ERROR("malformed header\n"); - return; + char buffer[8192]; + struct fuse_dirent *fde = (struct fuse_dirent*) buffer; + struct dirent *de; + struct dirhandle *h = id_to_ptr(req->fh); + + TRACE("[%d] READDIR %p\n", handler->token, h); + if (req->offset == 0) { + /* rewinddir() might have been called above us, so rewind here too */ + TRACE("[%d] calling rewinddir()\n", handler->token); + rewinddir(h->d); } + de = readdir(h->d); + if (!de) { + return 0; + } + fde->ino = FUSE_UNKNOWN_INO; + /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ + fde->off = req->offset + 1; + fde->type = de->d_type; + fde->namelen = strlen(de->d_name); + memcpy(fde->name, de->d_name, fde->namelen + 1); + fuse_reply(fuse, hdr->unique, fde, + FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); + return NO_STATUS; +} - len -= hdr->len; +static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_release_in* req) +{ + struct dirhandle *h = id_to_ptr(req->fh); - if (hdr->nodeid) { - node = lookup_by_inode(fuse, hdr->nodeid); - if (!node) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - } else { - node = 0; - } + TRACE("[%d] RELEASEDIR %p\n", handler->token, h); + closedir(h->d); + free(h); + return 0; +} + +static int handle_init(struct fuse* fuse, struct fuse_handler* handler, + const struct fuse_in_header* hdr, const struct fuse_init_in* req) +{ + struct fuse_init_out out; + + TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n", + handler->token, req->major, req->minor, req->max_readahead, req->flags); + out.major = FUSE_KERNEL_VERSION; + out.minor = FUSE_KERNEL_MINOR_VERSION; + out.max_readahead = req->max_readahead; + out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; + out.max_background = 32; + out.congestion_threshold = 32; + out.max_write = MAX_WRITE; + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} +static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler, + const struct fuse_in_header *hdr, const void *data, size_t data_len) +{ switch (hdr->opcode) { case FUSE_LOOKUP: { /* bytez[] -> entry_out */ - TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data); - lookup_entry(fuse, node, (char*) data, hdr->unique); - return; + const char* name = data; + return handle_lookup(fuse, handler, hdr, name); } + case FUSE_FORGET: { - struct fuse_forget_in *req = data; - TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup); - /* no reply */ - while (req->nlookup--) - node_release(node); - return; + const struct fuse_forget_in *req = data; + return handle_forget(fuse, handler, hdr, req); } - case FUSE_GETATTR: { /* getattr_in -> attr_out */ - struct fuse_getattr_in *req = data; - struct fuse_attr_out out; - - TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh); - memset(&out, 0, sizeof(out)); - node_get_attr(node, &out.attr); - out.attr_valid = 10; - - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_GETATTR: { /* getattr_in -> attr_out */ + const struct fuse_getattr_in *req = data; + return handle_getattr(fuse, handler, hdr, req); } - case FUSE_SETATTR: { /* setattr_in -> attr_out */ - struct fuse_setattr_in *req = data; - struct fuse_attr_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - int res = 0; - struct timespec times[2]; - - TRACE("SETATTR fh=%llx id=%llx valid=%x\n", - req->fh, hdr->nodeid, req->valid); - - /* XXX: incomplete implementation on purpose. chmod/chown - * should NEVER be implemented.*/ - - path = node_get_path(node, buffer, 0); - if (req->valid & FATTR_SIZE) - res = truncate(path, req->size); - if (res) - goto getout; - - /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW - * are both set, then set it to the current time. Else, set it to the - * time specified in the request. Same goes for mtime. Use utimensat(2) - * as it allows ATIME and MTIME to be changed independently, and has - * nanosecond resolution which fuse also has. - */ - if (req->valid & (FATTR_ATIME | FATTR_MTIME)) { - times[0].tv_nsec = UTIME_OMIT; - times[1].tv_nsec = UTIME_OMIT; - if (req->valid & FATTR_ATIME) { - if (req->valid & FATTR_ATIME_NOW) { - times[0].tv_nsec = UTIME_NOW; - } else { - times[0].tv_sec = req->atime; - times[0].tv_nsec = req->atimensec; - } - } - if (req->valid & FATTR_MTIME) { - if (req->valid & FATTR_MTIME_NOW) { - times[1].tv_nsec = UTIME_NOW; - } else { - times[1].tv_sec = req->mtime; - times[1].tv_nsec = req->mtimensec; - } - } - TRACE("Calling utimensat on %s with atime %ld, mtime=%ld\n", path, times[0].tv_sec, times[1].tv_sec); - res = utimensat(-1, path, times, 0); - } - getout: - memset(&out, 0, sizeof(out)); - node_get_attr(node, &out.attr); - out.attr_valid = 10; - - if (res) - fuse_status(fuse, hdr->unique, -errno); - else - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_SETATTR: { /* setattr_in -> attr_out */ + const struct fuse_setattr_in *req = data; + return handle_setattr(fuse, handler, hdr, req); } + // case FUSE_READLINK: // case FUSE_SYMLINK: case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */ - struct fuse_mknod_in *req = data; - char *path, buffer[PATH_BUFFER_SIZE]; - char *name = ((char*) data) + sizeof(*req); - int res; - - TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid); - path = node_get_path(node, buffer, name); - - req->mode = (req->mode & (~0777)) | 0664; - res = mknod(path, req->mode, req->rdev); /* XXX perm?*/ - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - } else { - lookup_entry(fuse, node, name, hdr->unique); - } - return; + const struct fuse_mknod_in *req = data; + const char *name = ((const char*) data) + sizeof(*req); + return handle_mknod(fuse, handler, hdr, req, name); } + case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */ - struct fuse_mkdir_in *req = data; - struct fuse_entry_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - char *name = ((char*) data) + sizeof(*req); - int res; - - TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode); - path = node_get_path(node, buffer, name); - - req->mode = (req->mode & (~0777)) | 0775; - res = mkdir(path, req->mode); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - } else { - lookup_entry(fuse, node, name, hdr->unique); - } - return; + const struct fuse_mkdir_in *req = data; + const char *name = ((const char*) data) + sizeof(*req); + return handle_mkdir(fuse, handler, hdr, req, name); } + case FUSE_UNLINK: { /* bytez[] -> */ - char *path, buffer[PATH_BUFFER_SIZE]; - int res; - TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid); - path = node_get_path(node, buffer, (char*) data); - res = unlink(path); - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + const char* name = data; + return handle_unlink(fuse, handler, hdr, name); } + case FUSE_RMDIR: { /* bytez[] -> */ - char *path, buffer[PATH_BUFFER_SIZE]; - int res; - TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid); - path = node_get_path(node, buffer, (char*) data); - res = rmdir(path); - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + const char* name = data; + return handle_rmdir(fuse, handler, hdr, name); } - case FUSE_RENAME: { /* rename_in, oldname, newname -> */ - struct fuse_rename_in *req = data; - char *oldname = ((char*) data) + sizeof(*req); - char *newname = oldname + strlen(oldname) + 1; - char *oldpath, oldbuffer[PATH_BUFFER_SIZE]; - char *newpath, newbuffer[PATH_BUFFER_SIZE]; - struct node *target; - struct node *newparent; - int res; - - TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid); - - target = lookup_child_by_name(node, oldname); - if (!target) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - oldpath = node_get_path(node, oldbuffer, oldname); - - newparent = lookup_by_inode(fuse, req->newdir); - if (!newparent) { - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - if (newparent == node) { - /* Special case for renaming a file where destination - * is same path differing only by case. - * In this case we don't want to look for a case insensitive match. - * This allows commands like "mv foo FOO" to work as expected. - */ - newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH); - } else { - newpath = node_get_path(newparent, newbuffer, newname); - } - if (!remove_child(node, target->nid)) { - ERROR("RENAME remove_child not found"); - fuse_status(fuse, hdr->unique, -ENOENT); - return; - } - if (!rename_node(target, newname)) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } - add_node_to_parent(target, newparent); - - res = rename(oldpath, newpath); - TRACE("RENAME result %d\n", res); - - fuse_status(fuse, hdr->unique, res ? -errno : 0); - return; + case FUSE_RENAME: { /* rename_in, oldname, newname -> */ + const struct fuse_rename_in *req = data; + const char *old_name = ((const char*) data) + sizeof(*req); + const char *new_name = old_name + strlen(old_name) + 1; + return handle_rename(fuse, handler, hdr, req, old_name, new_name); } -// case FUSE_LINK: - case FUSE_OPEN: { /* open_in -> open_out */ - struct fuse_open_in *req = data; - struct fuse_open_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - struct handle *h; - - h = malloc(sizeof(*h)); - if (!h) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } - path = node_get_path(node, buffer, 0); - TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h); - h->fd = open(path, req->flags); - if (h->fd < 0) { - ERROR("ERROR\n"); - fuse_status(fuse, hdr->unique, -errno); - free(h); - return; - } - out.fh = ptr_to_id(h); - out.open_flags = 0; - out.padding = 0; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; +// case FUSE_LINK: + case FUSE_OPEN: { /* open_in -> open_out */ + const struct fuse_open_in *req = data; + return handle_open(fuse, handler, hdr, req); } + case FUSE_READ: { /* read_in -> byte[] */ - char buffer[128 * 1024]; - struct fuse_read_in *req = data; - struct handle *h = id_to_ptr(req->fh); - int res; - TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); - if (req->size > sizeof(buffer)) { - fuse_status(fuse, hdr->unique, -EINVAL); - return; - } - res = pread64(h->fd, buffer, req->size, req->offset); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - fuse_reply(fuse, hdr->unique, buffer, res); - return; + const struct fuse_read_in *req = data; + return handle_read(fuse, handler, hdr, req); } + case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */ - struct fuse_write_in *req = data; - struct fuse_write_out out; - struct handle *h = id_to_ptr(req->fh); - int res; - TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); - res = pwrite64(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset); - if (res < 0) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - out.size = res; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - goto oops; + const struct fuse_write_in *req = data; + const void* buffer = (const __u8*)data + sizeof(*req); + return handle_write(fuse, handler, hdr, req, buffer); } - case FUSE_STATFS: { /* getattr_in -> attr_out */ - struct statfs stat; - struct fuse_statfs_out out; - int res; - TRACE("STATFS\n"); - - if (statfs(fuse->root.name, &stat)) { - fuse_status(fuse, hdr->unique, -errno); - return; - } - - memset(&out, 0, sizeof(out)); - out.st.blocks = stat.f_blocks; - out.st.bfree = stat.f_bfree; - out.st.bavail = stat.f_bavail; - out.st.files = stat.f_files; - out.st.ffree = stat.f_ffree; - out.st.bsize = stat.f_bsize; - out.st.namelen = stat.f_namelen; - out.st.frsize = stat.f_frsize; - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_STATFS: { /* getattr_in -> attr_out */ + return handle_statfs(fuse, handler, hdr); } + case FUSE_RELEASE: { /* release_in -> */ - struct fuse_release_in *req = data; - struct handle *h = id_to_ptr(req->fh); - TRACE("RELEASE %p(%d)\n", h, h->fd); - close(h->fd); - free(h); - fuse_status(fuse, hdr->unique, 0); - return; + const struct fuse_release_in *req = data; + return handle_release(fuse, handler, hdr, req); } -// case FUSE_FSYNC: + + case FUSE_FSYNC: { + const struct fuse_fsync_in *req = data; + return handle_fsync(fuse, handler, hdr, req); + } + // case FUSE_SETXATTR: // case FUSE_GETXATTR: // case FUSE_LISTXATTR: // case FUSE_REMOVEXATTR: - case FUSE_FLUSH: - fuse_status(fuse, hdr->unique, 0); - return; - case FUSE_OPENDIR: { /* open_in -> open_out */ - struct fuse_open_in *req = data; - struct fuse_open_out out; - char *path, buffer[PATH_BUFFER_SIZE]; - struct dirhandle *h; - - h = malloc(sizeof(*h)); - if (!h) { - fuse_status(fuse, hdr->unique, -ENOMEM); - return; - } + case FUSE_FLUSH: { + return handle_flush(fuse, handler, hdr); + } - path = node_get_path(node, buffer, 0); - TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path); - h->d = opendir(path); - if (h->d == 0) { - ERROR("ERROR\n"); - fuse_status(fuse, hdr->unique, -errno); - free(h); - return; - } - out.fh = ptr_to_id(h); - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + case FUSE_OPENDIR: { /* open_in -> open_out */ + const struct fuse_open_in *req = data; + return handle_opendir(fuse, handler, hdr, req); } + case FUSE_READDIR: { - struct fuse_read_in *req = data; - char buffer[8192]; - struct fuse_dirent *fde = (struct fuse_dirent*) buffer; - struct dirent *de; - struct dirhandle *h = id_to_ptr(req->fh); - TRACE("READDIR %p\n", h); - if (req->offset == 0) { - /* rewinddir() might have been called above us, so rewind here too */ - TRACE("calling rewinddir()\n"); - rewinddir(h->d); - } - de = readdir(h->d); - if (!de) { - fuse_status(fuse, hdr->unique, 0); - return; - } - fde->ino = FUSE_UNKNOWN_INO; - /* increment the offset so we can detect when rewinddir() seeks back to the beginning */ - fde->off = req->offset + 1; - fde->type = de->d_type; - fde->namelen = strlen(de->d_name); - memcpy(fde->name, de->d_name, fde->namelen + 1); - fuse_reply(fuse, hdr->unique, fde, - FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); - return; + const struct fuse_read_in *req = data; + return handle_readdir(fuse, handler, hdr, req); } + case FUSE_RELEASEDIR: { /* release_in -> */ - struct fuse_release_in *req = data; - struct dirhandle *h = id_to_ptr(req->fh); - TRACE("RELEASEDIR %p\n",h); - closedir(h->d); - free(h); - fuse_status(fuse, hdr->unique, 0); - return; + const struct fuse_release_in *req = data; + return handle_releasedir(fuse, handler, hdr, req); } + // case FUSE_FSYNCDIR: case FUSE_INIT: { /* init_in -> init_out */ - struct fuse_init_in *req = data; - struct fuse_init_out out; - - TRACE("INIT ver=%d.%d maxread=%d flags=%x\n", - req->major, req->minor, req->max_readahead, req->flags); - - out.major = FUSE_KERNEL_VERSION; - out.minor = FUSE_KERNEL_MINOR_VERSION; - out.max_readahead = req->max_readahead; - out.flags = FUSE_ATOMIC_O_TRUNC; - out.max_background = 32; - out.congestion_threshold = 32; - out.max_write = 256 * 1024; - - fuse_reply(fuse, hdr->unique, &out, sizeof(out)); - return; + const struct fuse_init_in *req = data; + return handle_init(fuse, handler, hdr, req); } + default: { - struct fuse_out_header h; - ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n", - hdr->opcode, hdr->unique, hdr->nodeid); - - oops: - h.len = sizeof(h); - h.error = -ENOSYS; - h.unique = hdr->unique; - write(fuse->fd, &h, sizeof(h)); - break; - } - } + TRACE("[%d] NOTIMPL op=%d uniq=%llx nid=%llx\n", + handler->token, hdr->opcode, hdr->unique, hdr->nodeid); + return -ENOSYS; + } + } } -void handle_fuse_requests(struct fuse *fuse) +static void handle_fuse_requests(struct fuse_handler* handler) { - unsigned char req[256 * 1024 + 128]; - int len; - + struct fuse* fuse = handler->fuse; for (;;) { - len = read(fuse->fd, req, 8192); + ssize_t len = read(fuse->fd, + handler->request_buffer, sizeof(handler->request_buffer)); if (len < 0) { - if (errno == EINTR) - continue; - ERROR("handle_fuse_requests: errno=%d\n", errno); - return; + if (errno != EINTR) { + ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno); + } + continue; + } + + if ((size_t)len < sizeof(struct fuse_in_header)) { + ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len); + continue; + } + + const struct fuse_in_header *hdr = (void*)handler->request_buffer; + if (hdr->len != (size_t)len) { + ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n", + handler->token, (size_t)len, hdr->len); + continue; + } + + const void *data = handler->request_buffer + sizeof(struct fuse_in_header); + size_t data_len = len - sizeof(struct fuse_in_header); + __u64 unique = hdr->unique; + int res = handle_fuse_request(fuse, handler, hdr, data, data_len); + + /* We do not access the request again after this point because the underlying + * buffer storage may have been reused while processing the request. */ + + if (res != NO_STATUS) { + if (res) { + TRACE("[%d] ERROR %d\n", handler->token, res); + } + fuse_status(fuse, unique, res); } - handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len); } } -static int usage() +static void* start_handler(void* data) { - ERROR("usage: sdcard [-l -f] <path> <uid> <gid>\n\n\t-l force file names to lower case when creating new files\n\t-f fix up file system before starting (repairs bad file name case and group ownership)\n"); - return -1; + struct fuse_handler* handler = data; + handle_fuse_requests(handler); + return NULL; } -int main(int argc, char **argv) +static int ignite_fuse(struct fuse* fuse, int num_threads) { - struct fuse fuse; - char opts[256]; - int fd; - int res; - const char *path = NULL; + struct fuse_handler* handlers; int i; - for (i = 1; i < argc; i++) { - char* arg = argv[i]; - if (!path) - path = arg; - else if (uid == -1) - uid = strtoul(arg, 0, 10); - else if (gid == -1) - gid = strtoul(arg, 0, 10); - else { - ERROR("too many arguments\n"); - return usage(); - } + handlers = malloc(num_threads * sizeof(struct fuse_handler)); + if (!handlers) { + ERROR("cannot allocate storage for threads"); + return -ENOMEM; } - if (!path) { - ERROR("no path specified\n"); - return usage(); + for (i = 0; i < num_threads; i++) { + handlers[i].fuse = fuse; + handlers[i].token = i; } - if (uid <= 0 || gid <= 0) { - ERROR("uid and gid must be nonzero\n"); - return usage(); + + for (i = 1; i < num_threads; i++) { + pthread_t thread; + int res = pthread_create(&thread, NULL, start_handler, &handlers[i]); + if (res) { + ERROR("failed to start thread #%d, error=%d", i, res); + goto quit; + } } + handle_fuse_requests(&handlers[0]); + ERROR("terminated prematurely"); + + /* don't bother killing all of the other threads or freeing anything, + * should never get here anyhow */ +quit: + exit(1); +} + +static int usage() +{ + ERROR("usage: sdcard [-t<threads>] <source_path> <dest_path> <uid> <gid>\n" + " -t<threads>: specify number of threads to use, default -t%d\n" + "\n", DEFAULT_NUM_THREADS); + return 1; +} + +static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t gid, + int num_threads) { + int fd; + char opts[256]; + int res; + struct fuse fuse; - /* cleanup from previous instance, if necessary */ - umount2(MOUNT_POINT, 2); + /* cleanup from previous instance, if necessary */ + umount2(dest_path, 2); fd = open("/dev/fuse", O_RDWR); if (fd < 0){ - ERROR("cannot open fuse device (%d)\n", errno); + ERROR("cannot open fuse device (error %d)\n", errno); return -1; } - sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other," - "user_id=%d,group_id=%d", fd, uid, gid); - - res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts); + snprintf(opts, sizeof(opts), + "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d", + fd, uid, gid); + + res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV, opts); if (res < 0) { - ERROR("cannot mount fuse filesystem (%d)\n", errno); - return -1; + ERROR("cannot mount fuse filesystem (error %d)\n", errno); + goto error; } - if (setgid(gid) < 0) { - ERROR("cannot setgid!\n"); - return -1; + res = setgid(gid); + if (res < 0) { + ERROR("cannot setgid (error %d)\n", errno); + goto error; } - if (setuid(uid) < 0) { - ERROR("cannot setuid!\n"); - return -1; + + res = setuid(uid); + if (res < 0) { + ERROR("cannot setuid (error %d)\n", errno); + goto error; } - fuse_init(&fuse, fd, path); + fuse_init(&fuse, fd, source_path); umask(0); - handle_fuse_requests(&fuse); - - return 0; + res = ignite_fuse(&fuse, num_threads); + + /* we do not attempt to umount the file system here because we are no longer + * running as the root user */ + +error: + close(fd); + return res; +} + +int main(int argc, char **argv) +{ + int res; + const char *source_path = NULL; + const char *dest_path = NULL; + uid_t uid = 0; + gid_t gid = 0; + int num_threads = DEFAULT_NUM_THREADS; + int i; + + for (i = 1; i < argc; i++) { + char* arg = argv[i]; + if (!strncmp(arg, "-t", 2)) + num_threads = strtoul(arg + 2, 0, 10); + else if (!source_path) + source_path = arg; + else if (!dest_path) + dest_path = arg; + else if (!uid) { + char* endptr = NULL; + errno = 0; + uid = strtoul(arg, &endptr, 10); + if (*endptr != '\0' || errno != 0) { + ERROR("Invalid uid"); + return usage(); + } + } else if (!gid) { + char* endptr = NULL; + errno = 0; + gid = strtoul(arg, &endptr, 10); + if (*endptr != '\0' || errno != 0) { + ERROR("Invalid gid"); + return usage(); + } + } else { + ERROR("too many arguments\n"); + return usage(); + } + } + + if (!source_path) { + ERROR("no source path specified\n"); + return usage(); + } + if (!dest_path) { + ERROR("no dest path specified\n"); + return usage(); + } + if (!uid || !gid) { + ERROR("uid and gid must be nonzero\n"); + return usage(); + } + if (num_threads < 1) { + ERROR("number of threads must be at least 1\n"); + return usage(); + } + + res = run(source_path, dest_path, uid, gid, num_threads); + return res < 0 ? 1 : 0; } @@ -687,14 +687,14 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd) struct cmdentry cmdentry; struct job *jp; struct jmploc jmploc; - struct jmploc *volatile savehandler; + struct jmploc *volatile savehandler = 0; char *volatile savecmdname; volatile struct shparam saveparam; struct localvar *volatile savelocalvars; volatile int e; char *lastarg; const char *path = pathval(); - volatile int temp_path; + volatile int temp_path = 0; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &argv; diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 9daeed3..dbbce06 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -55,11 +55,9 @@ TOOLS := \ nandread \ ionice \ touch \ - lsof - -ifeq ($(HAVE_SELINUX),true) - -TOOLS += \ + lsof \ + du \ + md5 \ getenforce \ setenforce \ chcon \ @@ -69,29 +67,31 @@ TOOLS += \ setsebool \ load_policy -endif - - ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r endif -LOCAL_SRC_FILES:= \ +ALL_TOOLS = $(TOOLS) +ALL_TOOLS += \ + cp \ + grep + +LOCAL_SRC_FILES := \ dynarray.c \ toolbox.c \ - $(patsubst %,%.c,$(TOOLS)) - -LOCAL_SHARED_LIBRARIES := libcutils libc libusbhost + $(patsubst %,%.c,$(TOOLS)) \ + cp/cp.c cp/utils.c \ + grep/grep.c grep/fastgrep.c grep/file.c grep/queue.c grep/util.c -ifeq ($(HAVE_SELINUX),true) +LOCAL_C_INCLUDES := bionic/libc/bionic -LOCAL_CFLAGS += -DHAVE_SELINUX -LOCAL_SHARED_LIBRARIES += libselinux -LOCAL_C_INCLUDES += external/libselinux/include - -endif +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libc \ + libusbhost \ + libselinux -LOCAL_MODULE:= toolbox +LOCAL_MODULE := toolbox # Including this will define $(intermediates). # @@ -100,7 +100,7 @@ include $(BUILD_EXECUTABLE) $(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h TOOLS_H := $(intermediates)/tools.h -$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS) +$(TOOLS_H): PRIVATE_TOOLS := $(ALL_TOOLS) $(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done $(TOOLS_H): $(LOCAL_PATH)/Android.mk $(TOOLS_H): @@ -108,7 +108,7 @@ $(TOOLS_H): # Make #!/system/bin/toolbox launchers for each tool. # -SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS)) +SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(ALL_TOOLS)) $(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE) $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> $(TOOLBOX_BINARY)" 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/cp/cp.c b/toolbox/cp/cp.c new file mode 100644 index 0000000..bd3c70e --- /dev/null +++ b/toolbox/cp/cp.c @@ -0,0 +1,552 @@ +/* $NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1988, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95"; +#else +__RCSID("$NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $"); +#endif +#endif /* not lint */ + +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-relative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <locale.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = '\0'; \ +} + +static char empty[] = ""; +PATH_T to = { .p_end = to.p_path, .target_end = empty }; + +uid_t myuid; +int Hflag, Lflag, Rflag, Pflag, fflag, iflag, lflag, pflag, rflag, vflag, Nflag; +mode_t myumask; +sig_atomic_t pinfo; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +static int copy(char *[], enum op, int); + +static void +progress(int sig __unused) +{ + + pinfo++; +} + +int +cp_main(int argc, char *argv[]) +{ + struct stat to_stat, tmp_stat; + enum op type; + int ch, fts_options, r, have_trailing_slash; + char *target, **src; + +#ifndef ANDROID + setprogname(argv[0]); +#endif + (void)setlocale(LC_ALL, ""); + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLNPRfailprv")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'N': + Nflag = 1; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'a': + Pflag = 1; + pflag = 1; + Rflag = 1; + Hflag = Lflag = 0; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + iflag = isatty(fileno(stdin)); + fflag = 0; + break; + case 'l': + lflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + cp_usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (argc < 2) + cp_usage(); + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) { + errx(EXIT_FAILURE, + "the -R and -r options may not be specified together."); + /* NOTREACHED */ + } + if (Hflag || Lflag || Pflag) { + errx(EXIT_FAILURE, + "the -H, -L, and -P options may not be specified with the -r option."); + /* NOTREACHED */ + } + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else if (!Pflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; + } + + myuid = getuid(); + + /* Copy the umask for explicit mode setting. */ + myumask = umask(0); + (void)umask(myumask); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) + errx(EXIT_FAILURE, "%s: name too long", target); + to.p_end = to.p_path + strlen(to.p_path); + have_trailing_slash = (to.p_end[-1] == '/'); + if (have_trailing_slash) + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + +#ifndef ANDROID + (void)signal(SIGINFO, progress); +#endif + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + if (Pflag) + r = lstat(to.p_path, &to_stat); + else + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) { + err(EXIT_FAILURE, "%s", to.p_path); + /* NOTREACHED */ + } + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) + cp_usage(); + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + r = stat(*argv, &tmp_stat); + else + r = lstat(*argv, &tmp_stat); + if (r == -1) { + err(EXIT_FAILURE, "%s", *argv); + /* NOTREACHED */ + } + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + + if (have_trailing_slash && type == FILE_TO_FILE) { + if (r == -1) + errx(1, "directory %s does not exist", + to.p_path); + else + errx(1, "%s is not a directory", to.p_path); + } + } else { + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + } + + /* + * make "cp -rp src/ dst" behave like "cp -rp src dst" not + * like "cp -rp src/. dst" + */ + for (src = argv; *src; src++) { + size_t len = strlen(*src); + while (len-- > 1 && (*src)[len] == '/') + (*src)[len] = '\0'; + } + + exit(copy(argv, type, fts_options)); + /* NOTREACHED */ +} + +static int dnestack[MAXPATHLEN]; /* unlikely we'll have more nested dirs */ +static ssize_t dnesp; +static void +pushdne(int dne) +{ + + dnestack[dnesp++] = dne; + assert(dnesp < MAXPATHLEN); +} + +static int +popdne(void) +{ + int rv; + + rv = dnestack[--dnesp]; + assert(dnesp >= 0); + return rv; +} + +static int +copy(char *argv[], enum op type, int fts_options) +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base, dne, sval; + int this_failed, any_failed; + size_t nlen; + char *p, *target_mid; + + base = 0; /* XXX gcc -Wuninitialized (see comment below) */ + + if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) + err(EXIT_FAILURE, "%s", argv[0]); + /* NOTREACHED */ + for (any_failed = 0; (curr = fts_read(ftsp)) != NULL;) { + this_failed = 0; + switch (curr->fts_info) { + case FTS_NS: + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", curr->fts_path, + strerror(curr->fts_errno)); + this_failed = any_failed = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + this_failed = any_failed = 1; + continue; + } + + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + if ((curr->fts_namelen + + to.target_end - to.p_path + 1) > MAXPATHLEN) { + warnx("%s/%s: name too long (not copied)", + to.p_path, curr->fts_name); + this_failed = any_failed = 1; + continue; + } + + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatentation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) { + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + target_mid = to.target_end; + if (*p != '/' && target_mid[-1] != '/') + *target_mid++ = '/'; + *target_mid = 0; + + if (target_mid - to.p_path + nlen >= PATH_MAX) { + warnx("%s%s: name too long (not copied)", + to.p_path, p); + this_failed = any_failed = 1; + continue; + } + (void)strncat(target_mid, p, nlen); + to.p_end = target_mid + nlen; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + sval = Pflag ? lstat(to.p_path, &to_stat) : stat(to.p_path, &to_stat); + /* Not an error but need to remember it happened */ + if (sval == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + this_failed = any_failed = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with non-directory %s", + to.p_path, curr->fts_path); + this_failed = any_failed = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + /* Catch special case of a non dangling symlink */ + if((fts_options & FTS_LOGICAL) || + ((fts_options & FTS_COMFOLLOW) && curr->fts_level == 0)) { + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + } else { + if (copy_link(curr, !dne)) + this_failed = any_failed = 1; + } + break; + case S_IFDIR: + if (!Rflag && !rflag) { + if (curr->fts_info == FTS_D) + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + this_failed = any_failed = 1; + break; + } + + /* + * Directories get noticed twice: + * In the first pass, create it if needed. + * In the second pass, after the children have been copied, set the permissions. + */ + if (curr->fts_info == FTS_D) /* First pass */ + { + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + pushdne(dne); + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) + err(EXIT_FAILURE, "%s", + to.p_path); + /* NOTREACHED */ + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + err(EXIT_FAILURE, "%s", + to.p_path); + /* NOTREACHED */ + } + } + else if (curr->fts_info == FTS_DP) /* Second pass */ + { + /* + * If not -p and directory didn't exist, set it to be + * the same as the from directory, umodified by the + * umask; arguably wrong, but it's been that way + * forever. + */ + if (pflag && setfile(curr->fts_statp, 0)) + this_failed = any_failed = 1; + else if ((dne = popdne())) + (void)chmod(to.p_path, + curr->fts_statp->st_mode); + } + else + { + warnx("directory %s encountered when not expected.", + curr->fts_path); + this_failed = any_failed = 1; + break; + } + + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + this_failed = any_failed = 1; + } else + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + this_failed = any_failed = 1; + } else + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + default: + if (copy_file(curr, dne)) + this_failed = any_failed = 1; + break; + } + if (vflag && !this_failed) + (void)printf("%s -> %s\n", curr->fts_path, to.p_path); + } + if (errno) { + err(EXIT_FAILURE, "fts_read"); + /* NOTREACHED */ + } + (void)fts_close(ftsp); + return (any_failed); +} diff --git a/toolbox/cp/extern.h b/toolbox/cp/extern.h new file mode 100644 index 0000000..ffbadf7 --- /dev/null +++ b/toolbox/cp/extern.h @@ -0,0 +1,61 @@ +/* $NetBSD: extern.h,v 1.17 2012/01/04 15:58:37 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + */ + +#ifndef _EXTERN_H_ +#define _EXTERN_H_ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */ +} PATH_T; + +extern PATH_T to; +extern uid_t myuid; +extern int Rflag, rflag, Hflag, Lflag, Pflag, fflag, iflag, lflag, pflag, Nflag; +extern mode_t myumask; +extern sig_atomic_t pinfo; + +#include <sys/cdefs.h> + +__BEGIN_DECLS +int copy_fifo(struct stat *, int); +int copy_file(FTSENT *, int); +int copy_link(FTSENT *, int); +int copy_special(struct stat *, int); +int set_utimes(const char *, struct stat *); +int setfile(struct stat *, int); +void cp_usage(void) __attribute__((__noreturn__)); +__END_DECLS + +#endif /* !_EXTERN_H_ */ diff --git a/toolbox/cp/utils.c b/toolbox/cp/utils.c new file mode 100644 index 0000000..b682bbe --- /dev/null +++ b/toolbox/cp/utils.c @@ -0,0 +1,446 @@ +/* $NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#else +__RCSID("$NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $"); +#endif +#endif /* not lint */ + +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#ifndef ANDROID +#include <sys/extattr.h> +#endif + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#ifdef ANDROID +#define MAXBSIZE 65536 +#endif + +#define MMAP_MAX_SIZE (8 * 1048576) +#define MMAP_MAX_WRITE (64 * 1024) + +int +set_utimes(const char *file, struct stat *fs) +{ + static struct timeval tv[2]; + +#ifdef ANDROID + tv[0].tv_sec = fs->st_atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = fs->st_mtime; + tv[1].tv_usec = 0; + + if (utimes(file, tv)) { + warn("utimes: %s", file); + return 1; + } +#else + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + + if (lutimes(file, tv)) { + warn("lutimes: %s", file); + return (1); + } +#endif + return (0); +} + +struct finfo { + const char *from; + const char *to; + size_t size; +}; + +static void +progress(const struct finfo *fi, size_t written) +{ + int pcent = (int)((100.0 * written) / fi->size); + + pinfo = 0; + (void)fprintf(stderr, "%s => %s %zu/%zu bytes %d%% written\n", + fi->from, fi->to, written, fi->size, pcent); +} + +int +copy_file(FTSENT *entp, int dne) +{ + static char buf[MAXBSIZE]; + struct stat to_stat, *fs; + int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount; + char *p; + size_t ptotal = 0; + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + return (1); + } + + to_fd = -1; + fs = entp->fts_statp; + tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag); + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { + struct stat sb; + int sval; + + if (iflag) { + (void)fprintf(stderr, "overwrite %s? ", to.p_path); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + return (0); + } + } + + sval = tolnk ? + lstat(to.p_path, &sb) : stat(to.p_path, &sb); + if (sval == -1) { + warn("stat: %s", to.p_path); + (void)close(from_fd); + return (1); + } + + if (!(tolnk && S_ISLNK(sb.st_mode))) + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } else + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + + if (to_fd == -1 && (fflag || tolnk)) { + /* + * attempt to remove existing destination file name and + * create a new file + */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + return (1); + } + + rval = 0; + + /* if hard linking then simply close the open fds, link and return */ + if (lflag) { + (void)close(from_fd); + (void)close(to_fd); + (void)unlink(to.p_path); + if (link(entp->fts_path, to.p_path)) { + warn("%s", to.p_path); + return (1); + } + return (0); + } + /* NOTREACHED */ + + /* + * There's no reason to do anything other than close the file + * now if it's empty, so let's not bother. + */ + if (fs->st_size > 0) { + struct finfo fi; + + fi.from = entp->fts_path; + fi.to = to.p_path; + fi.size = (size_t)fs->st_size; + + /* + * Mmap and write if less than 8M (the limit is so + * we don't totally trash memory on big files). + * This is really a minor hack, but it wins some CPU back. + */ + bool use_read; + + use_read = true; + if (fs->st_size <= MMAP_MAX_SIZE) { + size_t fsize = (size_t)fs->st_size; + p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED, + from_fd, (off_t)0); + if (p != MAP_FAILED) { + size_t remainder; + + use_read = false; + + (void) madvise(p, (size_t)fs->st_size, + MADV_SEQUENTIAL); + + /* + * Write out the data in small chunks to + * avoid locking the output file for a + * long time if the reading the data from + * the source is slow. + */ + remainder = fsize; + do { + ssize_t chunk; + + chunk = (remainder > MMAP_MAX_WRITE) ? + MMAP_MAX_WRITE : remainder; + if (write(to_fd, &p[fsize - remainder], + chunk) != chunk) { + warn("%s", to.p_path); + rval = 1; + break; + } + remainder -= chunk; + ptotal += chunk; + if (pinfo) + progress(&fi, ptotal); + } while (remainder > 0); + + if (munmap(p, fsize) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } + + if (use_read) { + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + wcount = write(to_fd, buf, (size_t)rcount); + if (rcount != wcount || wcount == -1) { + warn("%s", to.p_path); + rval = 1; + break; + } + ptotal += wcount; + if (pinfo) + progress(&fi, ptotal); + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } + +#ifndef ANDROID + if (pflag && (fcpxattr(from_fd, to_fd) != 0)) + warn("%s: error copying extended attributes", to.p_path); +#endif + + (void)close(from_fd); + + if (rval == 1) { + (void)close(to_fd); + return (1); + } + + if (pflag && setfile(fs, to_fd)) + rval = 1; + /* + * If the source was setuid or setgid, lose the bits unless the + * copy is owned by the same user and group. + */ +#define RETAINBITS \ + (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + if (!pflag && dne + && fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) { + if (fstat(to_fd, &to_stat)) { + warn("%s", to.p_path); + rval = 1; + } else if (fs->st_gid == to_stat.st_gid && + fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { + warn("%s", to.p_path); + rval = 1; + } + } + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + /* set the mod/access times now after close of the fd */ + if (pflag && set_utimes(to.p_path, fs)) { + rval = 1; + } + return (rval); +} + +int +copy_link(FTSENT *p, int exists) +{ + int len; + char target[MAXPATHLEN]; + + if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + target[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(target, to.p_path)) { + warn("symlink: %s", target); + return (1); + } + return (pflag ? setfile(p->fts_statp, 0) : 0); +} + +int +copy_fifo(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + +int +copy_special(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + + +/* + * Function: setfile + * + * Purpose: + * Set the owner/group/permissions for the "to" file to the information + * in the stat structure. If fd is zero, also call set_utimes() to set + * the mod/access times. If fd is non-zero, the caller must do a utimes + * itself after close(fd). + */ +int +setfile(struct stat *fs, int fd) +{ + int rval, islink; + + rval = 0; + islink = S_ISLNK(fs->st_mode); + fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : + lchown(to.p_path, fs->st_uid, fs->st_gid)) { + if (errno != EPERM) { + warn("chown: %s", to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } +#ifdef ANDROID + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { +#else + if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { +#endif + warn("chmod: %s", to.p_path); + rval = 1; + } + +#ifndef ANDROID + if (!islink && !Nflag) { + unsigned long fflags = fs->st_flags; + /* + * XXX + * NFS doesn't support chflags; ignore errors unless + * there's reason to believe we're losing bits. + * (Note, this still won't be right if the server + * supports flags and we were trying to *remove* flags + * on a file that we copied, i.e., that we didn't create.) + */ + errno = 0; + if ((fd ? fchflags(fd, fflags) : + chflags(to.p_path, fflags)) == -1) + if (errno != EOPNOTSUPP || fs->st_flags != 0) { + warn("chflags: %s", to.p_path); + rval = 1; + } + } +#endif + /* if fd is non-zero, caller must call set_utimes() after close() */ + if (fd == 0 && set_utimes(to.p_path, fs)) + rval = 1; + return (rval); +} + +void +cp_usage(void) +{ + (void)fprintf(stderr, + "usage: cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n" + " cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/toolbox/dd.c b/toolbox/dd.c index c6af3ea..a8c12d2 100644 --- a/toolbox/dd.c +++ b/toolbox/dd.c @@ -64,7 +64,7 @@ __RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $"); #include "dd.h" -#define NO_CONV +//#define NO_CONV //#include "extern.h" void block(void); @@ -91,13 +91,11 @@ extern u_int ddflags; extern u_int files_cnt; extern int progress; extern const u_char *ctab; -extern const u_char a2e_32V[], a2e_POSIX[]; -extern const u_char e2a_32V[], e2a_POSIX[]; -extern const u_char a2ibm_32V[], a2ibm_POSIX[]; -extern u_char casetab[]; +#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define DEFFILEMODE (S_IRUSR | S_IWUSR) static void dd_close(void); static void dd_in(void); @@ -188,14 +186,14 @@ setup(void) } else { #define OFLAGS \ (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) - out.fd = open(out.name, O_RDWR | OFLAGS /*, DEFFILEMODE */); + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); /* * May not have read access, so try again with write only. * Without read we may have a problem if output also does * not support seeks. */ if (out.fd < 0) { - out.fd = open(out.name, O_WRONLY | OFLAGS /*, DEFFILEMODE */); + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); out.flags |= NOREAD; } if (out.fd < 0) { @@ -243,42 +241,6 @@ setup(void) if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK)) (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); - /* - * If converting case at the same time as another conversion, build a - * table that does both at once. If just converting case, use the - * built-in tables. - */ - if (ddflags & (C_LCASE|C_UCASE)) { -#ifdef NO_CONV - /* Should not get here, but just in case... */ - fprintf(stderr, "case conv and -DNO_CONV\n"); - exit(1); - /* NOTREACHED */ -#else /* NO_CONV */ - u_int cnt; - - if (ddflags & C_ASCII || ddflags & C_EBCDIC) { - if (ddflags & C_LCASE) { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = tolower(ctab[cnt]); - } else { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = toupper(ctab[cnt]); - } - } else { - if (ddflags & C_LCASE) { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = tolower(cnt); - } else { - for (cnt = 0; cnt < 0377; ++cnt) - casetab[cnt] = toupper(cnt); - } - } - - ctab = casetab; -#endif /* NO_CONV */ - } - (void)gettimeofday(&st.start, NULL); /* Statistics timestamp. */ } @@ -628,8 +590,8 @@ pos_in(void) /* If not a pipe or tape device, try to seek on it. */ if (!(in.flags & (ISPIPE|ISTAPE))) { - if (lseek(in.fd, - (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) { + if (lseek64(in.fd, + (off64_t)in.offset * (off64_t)in.dbsz, SEEK_CUR) == -1) { fprintf(stderr, "%s: seek error: %s", in.name, strerror(errno)); exit(1); @@ -699,8 +661,8 @@ pos_out(void) * have specified the seek operand. */ if (!(out.flags & ISTAPE)) { - if (lseek(out.fd, - (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) { + if (lseek64(out.fd, + (off64_t)out.offset * (off64_t)out.dbsz, SEEK_SET) == -1) { fprintf(stderr, "%s: seek error: %s\n", out.name, strerror(errno)); exit(1); @@ -796,6 +758,9 @@ def(void) void def_close(void) { + if (ddflags & C_FDATASYNC) { + fdatasync(out.fd); + } /* Just update the count, everything is already in the buffer. */ if (in.dbcnt) @@ -1301,21 +1266,14 @@ static const struct conv { u_int set, noset; const u_char *ctab; } clist[] = { - { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, { "block", C_BLOCK, C_UNBLOCK, NULL }, - { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, - { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, - { "lcase", C_LCASE, C_UCASE, NULL }, + { "fdatasync", C_FDATASYNC, 0, NULL }, { "noerror", C_NOERROR, 0, NULL }, { "notrunc", C_NOTRUNC, 0, NULL }, - { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, - { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, - { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, { "osync", C_OSYNC, C_BS, NULL }, { "sparse", C_SPARSE, 0, NULL }, { "swab", C_SWAB, 0, NULL }, { "sync", C_SYNC, 0, NULL }, - { "ucase", C_UCASE, C_LCASE, NULL }, { "unblock", C_UNBLOCK, C_BLOCK, NULL }, /* If you add items to this table, be sure to add the * conversions to the C_BS check in the jcl routine above. diff --git a/toolbox/dd.h b/toolbox/dd.h index cca1024..89f2833 100644 --- a/toolbox/dd.h +++ b/toolbox/dd.h @@ -91,3 +91,4 @@ typedef struct { #define C_UNBLOCK 0x80000 #define C_OSYNC 0x100000 #define C_SPARSE 0x200000 +#define C_FDATASYNC 0x400000 diff --git a/toolbox/df.c b/toolbox/df.c index 63940a1..9cd0743 100644 --- a/toolbox/df.c +++ b/toolbox/df.c @@ -9,16 +9,22 @@ static int ok = EXIT_SUCCESS; static void printsize(long long n) { char unit = 'K'; - n /= 1024; - if (n > 1024) { + long long t; + + n *= 10; + + if (n > 1024*1024*10) { n /= 1024; unit = 'M'; } - if (n > 1024) { + + if (n > 1024*1024*10) { n /= 1024; unit = 'G'; } - printf("%4lld%c", n, unit); + + t = (n + 512) / 1024; + printf("%4lld.%1lld%c", t/10, t%10, unit); } static void df(char *s, int always) { @@ -41,7 +47,7 @@ static void df(char *s, int always) { } int df_main(int argc, char *argv[]) { - printf("Filesystem Size Used Free Blksize\n"); + printf("Filesystem Size Used Free Blksize\n"); if (argc == 1) { char s[2000]; FILE *f = fopen("/proc/mounts", "r"); diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c index e57f607..9c73b00 100644 --- a/toolbox/dmesg.c +++ b/toolbox/dmesg.c @@ -5,15 +5,30 @@ #include <sys/klog.h> #include <string.h> -#define KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT from our kernel */ -#define KLOG_BUF_LEN (1 << KLOG_BUF_SHIFT) +#define FALLBACK_KLOG_BUF_SHIFT 17 /* CONFIG_LOG_BUF_SHIFT from our kernel */ +#define FALLBACK_KLOG_BUF_LEN (1 << FALLBACK_KLOG_BUF_SHIFT) int dmesg_main(int argc, char **argv) { - char buffer[KLOG_BUF_LEN + 1]; - char *p = buffer; + char *buffer; + char *p; ssize_t ret; - int n, op; + int n, op, klog_buf_len; + + klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0); + + if (klog_buf_len <= 0) { + klog_buf_len = FALLBACK_KLOG_BUF_LEN; + } + + buffer = (char *)malloc(klog_buf_len + 1); + + if (!buffer) { + perror("malloc"); + return EXIT_FAILURE; + } + + p = buffer; if((argc == 2) && (!strcmp(argv[1],"-c"))) { op = KLOG_READ_CLEAR; @@ -21,7 +36,7 @@ int dmesg_main(int argc, char **argv) op = KLOG_READ_ALL; } - n = klogctl(op, buffer, KLOG_BUF_LEN); + n = klogctl(op, buffer, klog_buf_len); if (n < 0) { perror("klogctl"); return EXIT_FAILURE; diff --git a/toolbox/du.c b/toolbox/du.c new file mode 100644 index 0000000..06374a4 --- /dev/null +++ b/toolbox/du.c @@ -0,0 +1,322 @@ +/* $NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Newcomb. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: du.c,v 1.33 2008/07/30 22:03:40 dsl Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <util.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +int linkchk(dev_t, ino_t); +void prstat(const char *, int64_t); +void usage(void); + +long blocksize; + +#define howmany(x, y) (((x)+((y)-1))/(y)) + +int +du_main(int argc, char *argv[]) +{ + FTS *fts; + FTSENT *p; + int64_t totalblocks; + int ftsoptions, listfiles; + int depth; + int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag; + const char *noargv[2]; + + Hflag = Lflag = aflag = cflag = dflag = gkmflag = sflag = 0; + totalblocks = 0; + ftsoptions = FTS_PHYSICAL; + depth = INT_MAX; + while ((ch = getopt(argc, argv, "HLPacd:ghkmnrsx")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + depth = atoi(optarg); + if (depth < 0 || depth > SHRT_MAX) { + warnx("invalid argument to option d: %s", + optarg); + usage(); + } + break; + case 'g': + blocksize = 1024 * 1024 * 1024; + gkmflag = 1; + break; + case 'k': + blocksize = 1024; + gkmflag = 1; + break; + case 'm': + blocksize = 1024 * 1024; + gkmflag = 1; + break; + case 'r': + break; + case 's': + sflag = 1; + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * XXX + * Because of the way that fts(3) works, logical walks will not count + * the blocks actually used by symbolic links. We rationalize this by + * noting that users computing logical sizes are likely to do logical + * copies, so not counting the links is correct. The real reason is + * that we'd have to re-implement the kernel's symbolic link traversing + * algorithm to get this right. If, for example, you have relative + * symbolic links referencing other relative symbolic links, it gets + * very nasty, very fast. The bottom line is that it's documented in + * the man page, so it's a feature. + */ + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + if (Lflag) { + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + } + + listfiles = 0; + if (aflag) { + if (sflag || dflag) + usage(); + listfiles = 1; + } else if (sflag) { + if (dflag) + usage(); + depth = 0; + } + + if (!*argv) { + noargv[0] = "."; + noargv[1] = NULL; + argv = __UNCONST(noargv); + } + + if (!gkmflag) + blocksize = 512; + blocksize /= 512; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) + err(1, "fts_open `%s'", *argv); + + for (rval = 0; (p = fts_read(fts)) != NULL;) { + switch (p->fts_info) { + case FTS_D: /* Ignore. */ + break; + case FTS_DP: + p->fts_parent->fts_number += + p->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + /* + * If listing each directory, or not listing files + * or directories and this is post-order of the + * root of a traversal, display the total. + */ + if (p->fts_level <= depth + || (!listfiles && !p->fts_level)) + prstat(p->fts_path, p->fts_number); + break; + case FTS_DC: /* Ignore. */ + break; + case FTS_DNR: /* Warn, continue. */ + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + default: + if (p->fts_statp->st_nlink > 1 && + linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino)) + break; + /* + * If listing each file, or a non-directory file was + * the root of a traversal, display the total. + */ + if (listfiles || !p->fts_level) + prstat(p->fts_path, p->fts_statp->st_blocks); + p->fts_parent->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + } + } + if (errno) + err(1, "fts_read"); + if (cflag) + prstat("total", totalblocks); + exit(rval); +} + +void +prstat(const char *fname, int64_t blocks) +{ + (void)printf("%lld\t%s\n", + (long long)howmany(blocks, (int64_t)blocksize), + fname); +} + +int +linkchk(dev_t dev, ino_t ino) +{ + static struct entry { + dev_t dev; + ino_t ino; + } *htable; + static int htshift; /* log(allocated size) */ + static int htmask; /* allocated size - 1 */ + static int htused; /* 2*number of insertions */ + static int sawzero; /* Whether zero is in table or not */ + int h, h2; + uint64_t tmp; + /* this constant is (1<<64)/((1+sqrt(5))/2) + * aka (word size)/(golden ratio) + */ + const uint64_t HTCONST = 11400714819323198485ULL; + const int HTBITS = CHAR_BIT * sizeof(tmp); + + /* Never store zero in hashtable */ + if (dev == 0 && ino == 0) { + h = sawzero; + sawzero = 1; + return h; + } + + /* Extend hash table if necessary, keep load under 0.5 */ + if (htused<<1 >= htmask) { + struct entry *ohtable; + + if (!htable) + htshift = 10; /* starting hashtable size */ + else + htshift++; /* exponential hashtable growth */ + + htmask = (1 << htshift) - 1; + htused = 0; + + ohtable = htable; + htable = calloc(htmask+1, sizeof(*htable)); + if (!htable) + err(1, "calloc"); + + /* populate newly allocated hashtable */ + if (ohtable) { + int i; + for (i = 0; i <= htmask>>1; i++) + if (ohtable[i].ino || ohtable[i].dev) + linkchk(ohtable[i].dev, ohtable[i].ino); + free(ohtable); + } + } + + /* multiplicative hashing */ + tmp = dev; + tmp <<= HTBITS>>1; + tmp |= ino; + tmp *= HTCONST; + h = tmp >> (HTBITS - htshift); + h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ + + /* open address hashtable search with double hash probing */ + while (htable[h].ino || htable[h].dev) { + if ((htable[h].ino == ino) && (htable[h].dev == dev)) + return 1; + h = (h + h2) & htmask; + } + + /* Insert the current entry into hashtable */ + htable[h].dev = dev; + htable[h].ino = ino; + htused++; + return 0; +} + +void +usage(void) +{ + + (void)fprintf(stderr, + "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cgkmrx] [file ...]\n"); + exit(1); +} 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/grep/fastgrep.c b/toolbox/grep/fastgrep.c new file mode 100644 index 0000000..2fcd864 --- /dev/null +++ b/toolbox/grep/fastgrep.c @@ -0,0 +1,336 @@ +/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */ +/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * XXX: This file is a speed up for grep to cover the defects of the + * regex library. These optimizations should practically be implemented + * there keeping this code clean. This is a future TODO, but for the + * meantime, we need to use this workaround. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $"); + +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> + +#include "grep.h" + +static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t); +static inline void grep_revstr(unsigned char *, int); + +void +fgrepcomp(fastgrep_t *fg, const char *pat) +{ + unsigned int i; + + /* Initialize. */ + fg->len = strlen(pat); + fg->bol = false; + fg->eol = false; + fg->reversed = false; + + fg->pattern = (unsigned char *)grep_strdup(pat); + + /* Preprocess pattern. */ + for (i = 0; i <= UCHAR_MAX; i++) + fg->qsBc[i] = fg->len; + for (i = 1; i < fg->len; i++) + fg->qsBc[fg->pattern[i]] = fg->len - i; +} + +/* + * Returns: -1 on failure, 0 on success + */ +int +fastcomp(fastgrep_t *fg, const char *pat) +{ + unsigned int i; + int firstHalfDot = -1; + int firstLastHalfDot = -1; + int hasDot = 0; + int lastHalfDot = 0; + int shiftPatternLen; + + /* Initialize. */ + fg->len = strlen(pat); + fg->bol = false; + fg->eol = false; + fg->reversed = false; + fg->word = wflag; + + /* Remove end-of-line character ('$'). */ + if (fg->len > 0 && pat[fg->len - 1] == '$') { + fg->eol = true; + fg->len--; + } + + /* Remove beginning-of-line character ('^'). */ + if (pat[0] == '^') { + fg->bol = true; + fg->len--; + pat++; + } + + if (fg->len >= 14 && + memcmp(pat, "[[:<:]]", 7) == 0 && + memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) { + fg->len -= 14; + pat += 7; + /* Word boundary is handled separately in util.c */ + fg->word = true; + } + + /* + * pat has been adjusted earlier to not include '^', '$' or + * the word match character classes at the beginning and ending + * of the string respectively. + */ + fg->pattern = grep_malloc(fg->len + 1); + memcpy(fg->pattern, pat, fg->len); + fg->pattern[fg->len] = '\0'; + + /* Look for ways to cheat...er...avoid the full regex engine. */ + for (i = 0; i < fg->len; i++) { + /* Can still cheat? */ + if (fg->pattern[i] == '.') { + hasDot = i; + if (i < fg->len / 2) { + if (firstHalfDot < 0) + /* Closest dot to the beginning */ + firstHalfDot = i; + } else { + /* Closest dot to the end of the pattern. */ + lastHalfDot = i; + if (firstLastHalfDot < 0) + firstLastHalfDot = i; + } + } else { + /* Free memory and let others know this is empty. */ + free(fg->pattern); + fg->pattern = NULL; + return (-1); + } + } + + /* + * Determine if a reverse search would be faster based on the placement + * of the dots. + */ + if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) && + ((lastHalfDot) && ((firstHalfDot < 0) || + ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) && + !oflag && !color) { + fg->reversed = true; + hasDot = fg->len - (firstHalfDot < 0 ? + firstLastHalfDot : firstHalfDot) - 1; + grep_revstr(fg->pattern, fg->len); + } + + /* + * Normal Quick Search would require a shift based on the position the + * next character after the comparison is within the pattern. With + * wildcards, the position of the last dot effects the maximum shift + * distance. + * The closer to the end the wild card is the slower the search. A + * reverse version of this algorithm would be useful for wildcards near + * the end of the string. + * + * Examples: + * Pattern Max shift + * ------- --------- + * this 5 + * .his 4 + * t.is 3 + * th.s 2 + * thi. 1 + */ + + /* Adjust the shift based on location of the last dot ('.'). */ + shiftPatternLen = fg->len - hasDot; + + /* Preprocess pattern. */ + for (i = 0; i <= (signed)UCHAR_MAX; i++) + fg->qsBc[i] = shiftPatternLen; + for (i = hasDot + 1; i < fg->len; i++) { + fg->qsBc[fg->pattern[i]] = fg->len - i; + } + + /* + * Put pattern back to normal after pre-processing to allow for easy + * comparisons later. + */ + if (fg->reversed) + grep_revstr(fg->pattern, fg->len); + + return (0); +} + +int +grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch) +{ + unsigned int j; + int ret = REG_NOMATCH; + + if (pmatch->rm_so == (ssize_t)len) + return (ret); + + if (fg->bol && pmatch->rm_so != 0) { + pmatch->rm_so = len; + pmatch->rm_eo = len; + return (ret); + } + + /* No point in going farther if we do not have enough data. */ + if (len < fg->len) + return (ret); + + /* Only try once at the beginning or ending of the line. */ + if (fg->bol || fg->eol) { + /* Simple text comparison. */ + /* Verify data is >= pattern length before searching on it. */ + if (len >= fg->len) { + /* Determine where in data to start search at. */ + j = fg->eol ? len - fg->len : 0; + if (!((fg->bol && fg->eol) && (len != fg->len))) + if (grep_cmp(fg->pattern, data + j, + fg->len) == -1) { + pmatch->rm_so = j; + pmatch->rm_eo = j + fg->len; + ret = 0; + } + } + } else if (fg->reversed) { + /* Quick Search algorithm. */ + j = len; + do { + if (grep_cmp(fg->pattern, data + j - fg->len, + fg->len) == -1) { + pmatch->rm_so = j - fg->len; + pmatch->rm_eo = j; + ret = 0; + break; + } + /* Shift if within bounds, otherwise, we are done. */ + if (j == fg->len) + break; + j -= fg->qsBc[data[j - fg->len - 1]]; + } while (j >= fg->len); + } else { + /* Quick Search algorithm. */ + j = pmatch->rm_so; + do { + if (grep_cmp(fg->pattern, data + j, fg->len) == -1) { + pmatch->rm_so = j; + pmatch->rm_eo = j + fg->len; + ret = 0; + break; + } + + /* Shift if within bounds, otherwise, we are done. */ + if (j + fg->len == len) + break; + else + j += fg->qsBc[data[j + fg->len]]; + } while (j <= (len - fg->len)); + } + + return (ret); +} + +/* + * Returns: i >= 0 on failure (position that it failed) + * -1 on success + */ +static inline int +grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len) +{ + size_t size; + wchar_t *wdata, *wpat; + unsigned int i; + + if (iflag) { + if ((size = mbstowcs(NULL, (const char *)data, 0)) == + ((size_t) - 1)) + return (-1); + + wdata = grep_malloc(size * sizeof(wint_t)); + + if (mbstowcs(wdata, (const char *)data, size) == + ((size_t) - 1)) + return (-1); + + if ((size = mbstowcs(NULL, (const char *)pat, 0)) == + ((size_t) - 1)) + return (-1); + + wpat = grep_malloc(size * sizeof(wint_t)); + + if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1)) + return (-1); + for (i = 0; i < len; i++) { + if ((towlower(wpat[i]) == towlower(wdata[i])) || + ((grepbehave != GREP_FIXED) && wpat[i] == L'.')) + continue; + free(wpat); + free(wdata); + return (i); + } + } else { + for (i = 0; i < len; i++) { + if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) && + pat[i] == '.')) + continue; + return (i); + } + } + return (-1); +} + +static inline void +grep_revstr(unsigned char *str, int len) +{ + int i; + char c; + + for (i = 0; i < len / 2; i++) { + c = str[i]; + str[i] = str[len - i - 1]; + str[len - i - 1] = c; + } +} diff --git a/toolbox/grep/file.c b/toolbox/grep/file.c new file mode 100644 index 0000000..86b7658 --- /dev/null +++ b/toolbox/grep/file.c @@ -0,0 +1,269 @@ +/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */ +/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> + * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef ANDROID +#include <bzlib.h> +#endif +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> +#ifndef ANDROID +#include <zlib.h> +#endif + +#include "grep.h" + +#define MAXBUFSIZ (32 * 1024) +#define LNBUFBUMP 80 + +#ifndef ANDROID +static gzFile gzbufdesc; +static BZFILE* bzbufdesc; +#endif + +static unsigned char buffer[MAXBUFSIZ]; +static unsigned char *bufpos; +static size_t bufrem; + +static unsigned char *lnbuf; +static size_t lnbuflen; + +static inline int +grep_refill(struct file *f) +{ + ssize_t nr; + int bzerr; + + bufpos = buffer; + bufrem = 0; + +#ifndef ANDROID + if (filebehave == FILE_GZIP) + nr = gzread(gzbufdesc, buffer, MAXBUFSIZ); + else if (filebehave == FILE_BZIP && bzbufdesc != NULL) { + nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ); + switch (bzerr) { + case BZ_OK: + case BZ_STREAM_END: + /* No problem, nr will be okay */ + break; + case BZ_DATA_ERROR_MAGIC: + /* + * As opposed to gzread(), which simply returns the + * plain file data, if it is not in the correct + * compressed format, BZ2_bzRead() instead aborts. + * + * So, just restart at the beginning of the file again, + * and use plain reads from now on. + */ + BZ2_bzReadClose(&bzerr, bzbufdesc); + bzbufdesc = NULL; + if (lseek(f->fd, 0, SEEK_SET) == -1) + return (-1); + nr = read(f->fd, buffer, MAXBUFSIZ); + break; + default: + /* Make sure we exit with an error */ + nr = -1; + } + } else +#endif + nr = read(f->fd, buffer, MAXBUFSIZ); + + if (nr < 0) + return (-1); + + bufrem = nr; + return (0); +} + +static inline int +grep_lnbufgrow(size_t newlen) +{ + + if (lnbuflen < newlen) { + lnbuf = grep_realloc(lnbuf, newlen); + lnbuflen = newlen; + } + + return (0); +} + +char * +grep_fgetln(struct file *f, size_t *lenp) +{ + unsigned char *p; + char *ret; + size_t len; + size_t off; + ptrdiff_t diff; + + /* Fill the buffer, if necessary */ + if (bufrem == 0 && grep_refill(f) != 0) + goto error; + + if (bufrem == 0) { + /* Return zero length to indicate EOF */ + *lenp = 0; + return ((char *)bufpos); + } + + /* Look for a newline in the remaining part of the buffer */ + if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) { + ++p; /* advance over newline */ + ret = (char *)bufpos; + len = p - bufpos; + bufrem -= len; + bufpos = p; + *lenp = len; + return (ret); + } + + /* We have to copy the current buffered data to the line buffer */ + for (len = bufrem, off = 0; ; len += bufrem) { + /* Make sure there is room for more data */ + if (grep_lnbufgrow(len + LNBUFBUMP)) + goto error; + memcpy(lnbuf + off, bufpos, len - off); + off = len; + if (grep_refill(f) != 0) + goto error; + if (bufrem == 0) + /* EOF: return partial line */ + break; + if ((p = memchr(bufpos, line_sep, bufrem)) == NULL) + continue; + /* got it: finish up the line (like code above) */ + ++p; + diff = p - bufpos; + len += diff; + if (grep_lnbufgrow(len)) + goto error; + memcpy(lnbuf + off, bufpos, diff); + bufrem -= diff; + bufpos = p; + break; + } + *lenp = len; + return ((char *)lnbuf); + +error: + *lenp = 0; + return (NULL); +} + +static inline struct file * +grep_file_init(struct file *f) +{ + +#ifndef ANDROID + if (filebehave == FILE_GZIP && + (gzbufdesc = gzdopen(f->fd, "r")) == NULL) + goto error; + + if (filebehave == FILE_BZIP && + (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL) + goto error; +#endif + + /* Fill read buffer, also catches errors early */ + if (grep_refill(f) != 0) + goto error; + + /* Check for binary stuff, if necessary */ + if (!nulldataflag && binbehave != BINFILE_TEXT && + memchr(bufpos, '\0', bufrem) != NULL) + f->binary = true; + + return (f); +error: + close(f->fd); + free(f); + return (NULL); +} + +/* + * Opens a file for processing. + */ +struct file * +grep_open(const char *path) +{ + struct file *f; + + f = grep_malloc(sizeof *f); + memset(f, 0, sizeof *f); + if (path == NULL) { + /* Processing stdin implies --line-buffered. */ + lbflag = true; + f->fd = STDIN_FILENO; + } else if ((f->fd = open(path, O_RDONLY)) == -1) { + free(f); + return (NULL); + } + + return (grep_file_init(f)); +} + +/* + * Closes a file. + */ +void +grep_close(struct file *f) +{ + + close(f->fd); + + /* Reset read buffer and line buffer */ + bufpos = buffer; + bufrem = 0; + + free(lnbuf); + lnbuf = NULL; + lnbuflen = 0; +} diff --git a/toolbox/grep/grep.c b/toolbox/grep/grep.c new file mode 100644 index 0000000..b5bb2ef --- /dev/null +++ b/toolbox/grep/grep.c @@ -0,0 +1,710 @@ +/* $NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */ +/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $"); + +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <libgen.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "grep.h" + +#ifndef WITHOUT_NLS +#include <nl_types.h> +nl_catd catalog; +#endif + +/* + * Default messags to use when NLS is disabled or no catalogue + * is found. + */ +const char *errstr[] = { + "", +/* 1*/ "(standard input)", +/* 2*/ "cannot read bzip2 compressed file", +/* 3*/ "unknown %s option", +/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n", +/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", +/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", +/* 7*/ "\t[pattern] [file ...]\n", +/* 8*/ "Binary file %s matches\n", +/* 9*/ "%s (BSD grep) %s\n", +}; + +/* Flags passed to regcomp() and regexec() */ +int cflags = 0; +int eflags = REG_STARTEND; + +/* Searching patterns */ +unsigned int patterns, pattern_sz; +char **pattern; +regex_t *r_pattern; +fastgrep_t *fg_pattern; + +/* Filename exclusion/inclusion patterns */ +unsigned int fpatterns, fpattern_sz; +unsigned int dpatterns, dpattern_sz; +struct epat *dpattern, *fpattern; + +/* For regex errors */ +char re_error[RE_ERROR_BUF + 1]; + +/* Command-line flags */ +unsigned long long Aflag; /* -A x: print x lines trailing each match */ +unsigned long long Bflag; /* -B x: print x lines leading each match */ +bool Hflag; /* -H: always print file name */ +bool Lflag; /* -L: only show names of files with no matches */ +bool bflag; /* -b: show block numbers for each match */ +bool cflag; /* -c: only show a count of matching lines */ +bool hflag; /* -h: don't print filename headers */ +bool iflag; /* -i: ignore case */ +bool lflag; /* -l: only show names of files with matches */ +bool mflag; /* -m x: stop reading the files after x matches */ +unsigned long long mcount; /* count for -m */ +bool nflag; /* -n: show line numbers in front of matching lines */ +bool oflag; /* -o: print only matching part */ +bool qflag; /* -q: quiet mode (don't output anything) */ +bool sflag; /* -s: silent mode (ignore errors) */ +bool vflag; /* -v: only show non-matching lines */ +bool wflag; /* -w: pattern must start and end on word boundaries */ +bool xflag; /* -x: pattern must match entire line */ +bool lbflag; /* --line-buffered */ +bool nullflag; /* --null */ +bool nulldataflag; /* --null-data */ +unsigned char line_sep = '\n'; /* 0 for --null-data */ +char *label; /* --label */ +const char *color; /* --color */ +int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ +int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ +int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ +int devbehave = DEV_READ; /* -D: handling of devices */ +int dirbehave = DIR_READ; /* -dRr: handling of directories */ +int linkbehave = LINK_READ; /* -OpS: handling of symlinks */ + +bool dexclude, dinclude; /* --exclude-dir and --include-dir */ +bool fexclude, finclude; /* --exclude and --include */ + +enum { + BIN_OPT = CHAR_MAX + 1, + COLOR_OPT, + DECOMPRESS_OPT, + HELP_OPT, + MMAP_OPT, + LINEBUF_OPT, + LABEL_OPT, + R_EXCLUDE_OPT, + R_INCLUDE_OPT, + R_DEXCLUDE_OPT, + R_DINCLUDE_OPT +}; + +static inline const char *init_color(const char *); + +/* Housekeeping */ +int tail; /* lines left to print */ +bool notfound; /* file not found */ + +extern char *__progname; + +/* + * Prints usage information and returns 2. + */ +__dead static void +usage(void) +{ + fprintf(stderr, getstr(4), __progname); + fprintf(stderr, "%s", getstr(5)); + fprintf(stderr, "%s", getstr(5)); + fprintf(stderr, "%s", getstr(6)); + fprintf(stderr, "%s", getstr(7)); + exit(2); +} + +static const char optstr[] = + "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz"; + +struct option long_options[] = +{ + {"binary-files", required_argument, NULL, BIN_OPT}, + {"decompress", no_argument, NULL, DECOMPRESS_OPT}, + {"help", no_argument, NULL, HELP_OPT}, + {"mmap", no_argument, NULL, MMAP_OPT}, + {"line-buffered", no_argument, NULL, LINEBUF_OPT}, + {"label", required_argument, NULL, LABEL_OPT}, + {"color", optional_argument, NULL, COLOR_OPT}, + {"colour", optional_argument, NULL, COLOR_OPT}, + {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, + {"include", required_argument, NULL, R_INCLUDE_OPT}, + {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, + {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, + {"after-context", required_argument, NULL, 'A'}, + {"text", no_argument, NULL, 'a'}, + {"before-context", required_argument, NULL, 'B'}, + {"byte-offset", no_argument, NULL, 'b'}, + {"context", optional_argument, NULL, 'C'}, + {"count", no_argument, NULL, 'c'}, + {"devices", required_argument, NULL, 'D'}, + {"directories", required_argument, NULL, 'd'}, + {"extended-regexp", no_argument, NULL, 'E'}, + {"regexp", required_argument, NULL, 'e'}, + {"fixed-strings", no_argument, NULL, 'F'}, + {"file", required_argument, NULL, 'f'}, + {"basic-regexp", no_argument, NULL, 'G'}, + {"no-filename", no_argument, NULL, 'h'}, + {"with-filename", no_argument, NULL, 'H'}, + {"ignore-case", no_argument, NULL, 'i'}, + {"bz2decompress", no_argument, NULL, 'J'}, + {"files-with-matches", no_argument, NULL, 'l'}, + {"files-without-match", no_argument, NULL, 'L'}, + {"max-count", required_argument, NULL, 'm'}, + {"line-number", no_argument, NULL, 'n'}, + {"only-matching", no_argument, NULL, 'o'}, + {"quiet", no_argument, NULL, 'q'}, + {"silent", no_argument, NULL, 'q'}, + {"recursive", no_argument, NULL, 'r'}, + {"no-messages", no_argument, NULL, 's'}, + {"binary", no_argument, NULL, 'U'}, + {"unix-byte-offsets", no_argument, NULL, 'u'}, + {"invert-match", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"word-regexp", no_argument, NULL, 'w'}, + {"line-regexp", no_argument, NULL, 'x'}, + {"null", no_argument, NULL, 'Z'}, + {"null-data", no_argument, NULL, 'z'}, + {NULL, no_argument, NULL, 0} +}; + +/* + * Adds a searching pattern to the internal array. + */ +static void +add_pattern(char *pat, size_t len) +{ + + /* TODO: Check for empty patterns and shortcut */ + + /* Increase size if necessary */ + if (patterns == pattern_sz) { + pattern_sz *= 2; + pattern = grep_realloc(pattern, ++pattern_sz * + sizeof(*pattern)); + } + if (len > 0 && pat[len - 1] == '\n') + --len; + /* pat may not be NUL-terminated */ + pattern[patterns] = grep_malloc(len + 1); + memcpy(pattern[patterns], pat, len); + pattern[patterns][len] = '\0'; + ++patterns; +} + +/* + * Adds a file include/exclude pattern to the internal array. + */ +static void +add_fpattern(const char *pat, int mode) +{ + + /* Increase size if necessary */ + if (fpatterns == fpattern_sz) { + fpattern_sz *= 2; + fpattern = grep_realloc(fpattern, ++fpattern_sz * + sizeof(struct epat)); + } + fpattern[fpatterns].pat = grep_strdup(pat); + fpattern[fpatterns].mode = mode; + ++fpatterns; +} + +/* + * Adds a directory include/exclude pattern to the internal array. + */ +static void +add_dpattern(const char *pat, int mode) +{ + + /* Increase size if necessary */ + if (dpatterns == dpattern_sz) { + dpattern_sz *= 2; + dpattern = grep_realloc(dpattern, ++dpattern_sz * + sizeof(struct epat)); + } + dpattern[dpatterns].pat = grep_strdup(pat); + dpattern[dpatterns].mode = mode; + ++dpatterns; +} + +/* + * Reads searching patterns from a file and adds them with add_pattern(). + */ +static void +read_patterns(const char *fn) +{ + FILE *f; + char *line; + size_t len; + ssize_t rlen; + + if ((f = fopen(fn, "r")) == NULL) + err(2, "%s", fn); + line = NULL; + len = 0; +#ifndef ANDROID + while ((rlen = getline(&line, &len, f)) != -1) + add_pattern(line, *line == '\n' ? 0 : (size_t)rlen); +#endif + free(line); + if (ferror(f)) + err(2, "%s", fn); + fclose(f); +} + +static inline const char * +init_color(const char *d) +{ + char *c; + + c = getenv("GREP_COLOR"); + return (c != NULL ? c : d); +} + +int +grep_main(int argc, char *argv[]) +{ + char **aargv, **eargv, *eopts; + char *ep; + unsigned long long l; + unsigned int aargc, eargc, i, j; + int c, lastc, needpattern, newarg, prevoptind; + + setlocale(LC_ALL, ""); + +#ifndef WITHOUT_NLS + catalog = catopen("grep", NL_CAT_LOCALE); +#endif + + /* Check what is the program name of the binary. In this + way we can have all the funcionalities in one binary + without the need of scripting and using ugly hacks. */ + switch (__progname[0]) { + case 'e': + grepbehave = GREP_EXTENDED; + break; + case 'f': + grepbehave = GREP_FIXED; + break; + case 'g': + grepbehave = GREP_BASIC; + break; + case 'z': + filebehave = FILE_GZIP; + switch(__progname[1]) { + case 'e': + grepbehave = GREP_EXTENDED; + break; + case 'f': + grepbehave = GREP_FIXED; + break; + case 'g': + grepbehave = GREP_BASIC; + break; + } + break; + } + + lastc = '\0'; + newarg = 1; + prevoptind = 1; + needpattern = 1; + + eopts = getenv("GREP_OPTIONS"); + + /* support for extra arguments in GREP_OPTIONS */ + eargc = 0; + if (eopts != NULL) { + char *str; + + /* make an estimation of how many extra arguments we have */ + for (j = 0; j < strlen(eopts); j++) + if (eopts[j] == ' ') + eargc++; + + eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); + + eargc = 0; + /* parse extra arguments */ + while ((str = strsep(&eopts, " ")) != NULL) + eargv[eargc++] = grep_strdup(str); + + aargv = (char **)grep_calloc(eargc + argc + 1, + sizeof(char *)); + + aargv[0] = argv[0]; + for (i = 0; i < eargc; i++) + aargv[i + 1] = eargv[i]; + for (j = 1; j < (unsigned int)argc; j++, i++) + aargv[i + 1] = argv[j]; + + aargc = eargc + argc; + } else { + aargv = argv; + aargc = argc; + } + + while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != + -1)) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (newarg || !isdigit(lastc)) + Aflag = 0; + else if (Aflag > LLONG_MAX / 10) { + errno = ERANGE; + err(2, NULL); + } + Aflag = Bflag = (Aflag * 10) + (c - '0'); + break; + case 'C': + if (optarg == NULL) { + Aflag = Bflag = 2; + break; + } + /* FALLTHROUGH */ + case 'A': + /* FALLTHROUGH */ + case 'B': + errno = 0; + l = strtoull(optarg, &ep, 10); + if (((errno == ERANGE) && (l == ULLONG_MAX)) || + ((errno == EINVAL) && (l == 0))) + err(2, NULL); + else if (ep[0] != '\0') { + errno = EINVAL; + err(2, NULL); + } + if (c == 'A') + Aflag = l; + else if (c == 'B') + Bflag = l; + else + Aflag = Bflag = l; + break; + case 'a': + binbehave = BINFILE_TEXT; + break; + case 'b': + bflag = true; + break; + case 'c': + cflag = true; + break; + case 'D': + if (strcasecmp(optarg, "skip") == 0) + devbehave = DEV_SKIP; + else if (strcasecmp(optarg, "read") == 0) + devbehave = DEV_READ; + else + errx(2, getstr(3), "--devices"); + break; + case 'd': + if (strcasecmp("recurse", optarg) == 0) { + Hflag = true; + dirbehave = DIR_RECURSE; + } else if (strcasecmp("skip", optarg) == 0) + dirbehave = DIR_SKIP; + else if (strcasecmp("read", optarg) == 0) + dirbehave = DIR_READ; + else + errx(2, getstr(3), "--directories"); + break; + case 'E': + grepbehave = GREP_EXTENDED; + break; + case 'e': + add_pattern(optarg, strlen(optarg)); + needpattern = 0; + break; + case 'F': + grepbehave = GREP_FIXED; + break; + case 'f': + read_patterns(optarg); + needpattern = 0; + break; + case 'G': + grepbehave = GREP_BASIC; + break; + case 'H': + Hflag = true; + break; + case 'h': + Hflag = false; + hflag = true; + break; + case 'I': + binbehave = BINFILE_SKIP; + break; + case 'i': + case 'y': + iflag = true; + cflags |= REG_ICASE; + break; + case 'J': + filebehave = FILE_BZIP; + break; + case 'L': + lflag = false; + Lflag = true; + break; + case 'l': + Lflag = false; + lflag = true; + break; + case 'm': + mflag = true; + errno = 0; + mcount = strtoull(optarg, &ep, 10); + if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || + ((errno == EINVAL) && (mcount == 0))) + err(2, NULL); + else if (ep[0] != '\0') { + errno = EINVAL; + err(2, NULL); + } + break; + case 'n': + nflag = true; + break; + case 'O': + linkbehave = LINK_EXPLICIT; + break; + case 'o': + oflag = true; + break; + case 'p': + linkbehave = LINK_SKIP; + break; + case 'q': + qflag = true; + break; + case 'S': + linkbehave = LINK_READ; + break; + case 'R': + case 'r': + dirbehave = DIR_RECURSE; + Hflag = true; + break; + case 's': + sflag = true; + break; + case 'U': + binbehave = BINFILE_BIN; + break; + case 'u': + case MMAP_OPT: + /* noop, compatibility */ + break; + case 'V': + printf(getstr(9), __progname, VERSION); + exit(0); + case 'v': + vflag = true; + break; + case 'w': + wflag = true; + break; + case 'x': + xflag = true; + break; + case 'Z': + nullflag = true; + break; + case 'z': + nulldataflag = true; + line_sep = '\0'; + break; + case BIN_OPT: + if (strcasecmp("binary", optarg) == 0) + binbehave = BINFILE_BIN; + else if (strcasecmp("without-match", optarg) == 0) + binbehave = BINFILE_SKIP; + else if (strcasecmp("text", optarg) == 0) + binbehave = BINFILE_TEXT; + else + errx(2, getstr(3), "--binary-files"); + break; + case COLOR_OPT: + color = NULL; + if (optarg == NULL || strcasecmp("auto", optarg) == 0 || + strcasecmp("tty", optarg) == 0 || + strcasecmp("if-tty", optarg) == 0) { + char *term; + + term = getenv("TERM"); + if (isatty(STDOUT_FILENO) && term != NULL && + strcasecmp(term, "dumb") != 0) + color = init_color("01;31"); + } else if (strcasecmp("always", optarg) == 0 || + strcasecmp("yes", optarg) == 0 || + strcasecmp("force", optarg) == 0) { + color = init_color("01;31"); + } else if (strcasecmp("never", optarg) != 0 && + strcasecmp("none", optarg) != 0 && + strcasecmp("no", optarg) != 0) + errx(2, getstr(3), "--color"); + break; + case DECOMPRESS_OPT: + filebehave = FILE_GZIP; + break; + case LABEL_OPT: + label = optarg; + break; + case LINEBUF_OPT: + lbflag = true; + break; + case R_INCLUDE_OPT: + finclude = true; + add_fpattern(optarg, INCL_PAT); + break; + case R_EXCLUDE_OPT: + fexclude = true; + add_fpattern(optarg, EXCL_PAT); + break; + case R_DINCLUDE_OPT: + dinclude = true; + add_dpattern(optarg, INCL_PAT); + break; + case R_DEXCLUDE_OPT: + dexclude = true; + add_dpattern(optarg, EXCL_PAT); + break; + case HELP_OPT: + default: + usage(); + } + lastc = c; + newarg = optind != prevoptind; + prevoptind = optind; + } + aargc -= optind; + aargv += optind; + + /* Fail if we don't have any pattern */ + if (aargc == 0 && needpattern) + usage(); + + /* Process patterns from command line */ + if (aargc != 0 && needpattern) { + add_pattern(*aargv, strlen(*aargv)); + --aargc; + ++aargv; + } + + switch (grepbehave) { + case GREP_FIXED: + case GREP_BASIC: + break; + case GREP_EXTENDED: + cflags |= REG_EXTENDED; + break; + default: + /* NOTREACHED */ + usage(); + } + + fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); + r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); +/* + * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. + * Optimizations should be done there. + */ + /* Check if cheating is allowed (always is for fgrep). */ + if (grepbehave == GREP_FIXED) { + for (i = 0; i < patterns; ++i) + fgrepcomp(&fg_pattern[i], pattern[i]); + } else { + for (i = 0; i < patterns; ++i) { + if (fastcomp(&fg_pattern[i], pattern[i])) { + /* Fall back to full regex library */ + c = regcomp(&r_pattern[i], pattern[i], cflags); + if (c != 0) { + regerror(c, &r_pattern[i], re_error, + RE_ERROR_BUF); + errx(2, "%s", re_error); + } + } + } + } + + if (lbflag) + setlinebuf(stdout); + + if ((aargc == 0 || aargc == 1) && !Hflag) + hflag = true; + + if (aargc == 0) + exit(!procfile("-")); + + if (dirbehave == DIR_RECURSE) + c = grep_tree(aargv); + else + for (c = 0; aargc--; ++aargv) { + if ((finclude || fexclude) && !file_matching(*aargv)) + continue; + c+= procfile(*aargv); + } + +#ifndef WITHOUT_NLS + catclose(catalog); +#endif + + /* Find out the correct return value according to the + results and the command line option. */ + exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); +} diff --git a/toolbox/grep/grep.h b/toolbox/grep/grep.h new file mode 100644 index 0000000..6454f93 --- /dev/null +++ b/toolbox/grep/grep.h @@ -0,0 +1,166 @@ +/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */ +/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */ +/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef ANDROID +#define WITHOUT_NLS +#endif + +#ifndef ANDROID +#include <bzlib.h> +#endif +#include <limits.h> +#include <regex.h> +#include <stdbool.h> +#include <stdio.h> +#ifndef ANDROID +#include <zlib.h> +#endif + +#ifdef WITHOUT_NLS +#define getstr(n) errstr[n] +#else +#include <nl_types.h> + +extern nl_catd catalog; +#define getstr(n) catgets(catalog, 1, n, errstr[n]) +#endif + +extern const char *errstr[]; + +#define VERSION "2.5.1-FreeBSD" + +#define GREP_FIXED 0 +#define GREP_BASIC 1 +#define GREP_EXTENDED 2 + +#define BINFILE_BIN 0 +#define BINFILE_SKIP 1 +#define BINFILE_TEXT 2 + +#define FILE_STDIO 0 +#define FILE_GZIP 1 +#define FILE_BZIP 2 + +#define DIR_READ 0 +#define DIR_SKIP 1 +#define DIR_RECURSE 2 + +#define DEV_READ 0 +#define DEV_SKIP 1 + +#define LINK_READ 0 +#define LINK_EXPLICIT 1 +#define LINK_SKIP 2 + +#define EXCL_PAT 0 +#define INCL_PAT 1 + +#define MAX_LINE_MATCHES 32 + +struct file { + int fd; + bool binary; +}; + +struct str { + off_t off; + size_t len; + char *dat; + char *file; + int line_no; +}; + +struct epat { + char *pat; + int mode; +}; + +typedef struct { + size_t len; + unsigned char *pattern; + int qsBc[UCHAR_MAX + 1]; + /* flags */ + bool bol; + bool eol; + bool reversed; + bool word; +} fastgrep_t; + +/* Flags passed to regcomp() and regexec() */ +extern int cflags, eflags; + +/* Command line flags */ +extern bool Eflag, Fflag, Gflag, Hflag, Lflag, + bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag, + qflag, sflag, vflag, wflag, xflag; +extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag; +extern unsigned char line_sep; +extern unsigned long long Aflag, Bflag, mcount; +extern char *label; +extern const char *color; +extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave; + +extern bool notfound; +extern int tail; +extern unsigned int dpatterns, fpatterns, patterns; +extern char **pattern; +extern struct epat *dpattern, *fpattern; +extern regex_t *er_pattern, *r_pattern; +extern fastgrep_t *fg_pattern; + +/* For regex errors */ +#define RE_ERROR_BUF 512 +extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */ + +/* util.c */ +bool file_matching(const char *fname); +int procfile(const char *fn); +int grep_tree(char **argv); +void *grep_malloc(size_t size); +void *grep_calloc(size_t nmemb, size_t size); +void *grep_realloc(void *ptr, size_t size); +char *grep_strdup(const char *str); +void printline(struct str *line, int sep, regmatch_t *matches, int m); + +/* queue.c */ +void enqueue(struct str *x); +void printqueue(void); +void clearqueue(void); + +/* file.c */ +void grep_close(struct file *f); +struct file *grep_open(const char *path); +char *grep_fgetln(struct file *f, size_t *len); + +/* fastgrep.c */ +int fastcomp(fastgrep_t *, const char *); +void fgrepcomp(fastgrep_t *, const char *); +int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *); diff --git a/toolbox/grep/queue.c b/toolbox/grep/queue.c new file mode 100644 index 0000000..e3c6be1 --- /dev/null +++ b/toolbox/grep/queue.c @@ -0,0 +1,116 @@ +/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */ +/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */ +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * A really poor man's queue. It does only what it has to and gets out of + * Dodge. It is used in place of <sys/queue.h> to get a better performance. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $"); + +#include <sys/param.h> +#include <sys/queue.h> + +#include <stdlib.h> +#include <string.h> + +#include "grep.h" + +struct qentry { + STAILQ_ENTRY(qentry) list; + struct str data; +}; + +static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue); +static unsigned long long count; + +static struct qentry *dequeue(void); + +void +enqueue(struct str *x) +{ + struct qentry *item; + + item = grep_malloc(sizeof(struct qentry)); + item->data.dat = grep_malloc(sizeof(char) * x->len); + item->data.len = x->len; + item->data.line_no = x->line_no; + item->data.off = x->off; + memcpy(item->data.dat, x->dat, x->len); + item->data.file = x->file; + + STAILQ_INSERT_TAIL(&queue, item, list); + + if (++count > Bflag) { + item = dequeue(); + free(item->data.dat); + free(item); + } +} + +static struct qentry * +dequeue(void) +{ + struct qentry *item; + + item = STAILQ_FIRST(&queue); + if (item == NULL) + return (NULL); + + STAILQ_REMOVE_HEAD(&queue, list); + --count; + return (item); +} + +void +printqueue(void) +{ + struct qentry *item; + + while ((item = dequeue()) != NULL) { + printline(&item->data, '-', NULL, 0); + free(item->data.dat); + free(item); + } +} + +void +clearqueue(void) +{ + struct qentry *item; + + while ((item = dequeue()) != NULL) { + free(item->data.dat); + free(item); + } +} diff --git a/toolbox/grep/util.c b/toolbox/grep/util.c new file mode 100644 index 0000000..497db06 --- /dev/null +++ b/toolbox/grep/util.c @@ -0,0 +1,498 @@ +/* $NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $ */ +/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */ +/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */ + +/*- + * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav + * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: util.c,v 1.16 2012/05/06 22:32:05 joerg Exp $"); + +#include <sys/stat.h> +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fnmatch.h> +#include <fts.h> +#include <libgen.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +#include "grep.h" + +static bool first, first_global = true; +static unsigned long long since_printed; + +static int procline(struct str *l, int); + +bool +file_matching(const char *fname) +{ + char *fname_base, *fname_copy; + unsigned int i; + bool ret; + + ret = finclude ? false : true; + fname_copy = grep_strdup(fname); + fname_base = basename(fname_copy); + + for (i = 0; i < fpatterns; ++i) { + if (fnmatch(fpattern[i].pat, fname, 0) == 0 || + fnmatch(fpattern[i].pat, fname_base, 0) == 0) { + if (fpattern[i].mode == EXCL_PAT) + return (false); + else + ret = true; + } + } + free(fname_copy); + return (ret); +} + +static inline bool +dir_matching(const char *dname) +{ + unsigned int i; + bool ret; + + ret = dinclude ? false : true; + + for (i = 0; i < dpatterns; ++i) { + if (dname != NULL && + fnmatch(dname, dpattern[i].pat, 0) == 0) { + if (dpattern[i].mode == EXCL_PAT) + return (false); + else + ret = true; + } + } + return (ret); +} + +/* + * Processes a directory when a recursive search is performed with + * the -R option. Each appropriate file is passed to procfile(). + */ +int +grep_tree(char **argv) +{ + FTS *fts; + FTSENT *p; + char *d, *dir = NULL; + int c, fts_flags; + bool ok; + + c = fts_flags = 0; + + switch(linkbehave) { + case LINK_EXPLICIT: + fts_flags = FTS_COMFOLLOW; + break; + case LINK_SKIP: + fts_flags = FTS_PHYSICAL; + break; + default: + fts_flags = FTS_LOGICAL; + + } + + fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; + + if (!(fts = fts_open(argv, fts_flags, NULL))) + err(2, "fts_open"); + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + /* FALLTHROUGH */ + case FTS_ERR: + errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno)); + break; + case FTS_D: + /* FALLTHROUGH */ + case FTS_DP: + break; + case FTS_DC: + /* Print a warning for recursive directory loop */ + warnx("warning: %s: recursive directory loop", + p->fts_path); + break; + default: + /* Check for file exclusion/inclusion */ + ok = true; + if (dexclude || dinclude) { + if ((d = strrchr(p->fts_path, '/')) != NULL) { + dir = grep_malloc(sizeof(char) * + (d - p->fts_path + 1)); + memcpy(dir, p->fts_path, + d - p->fts_path); + dir[d - p->fts_path] = '\0'; + } + ok = dir_matching(dir); + free(dir); + dir = NULL; + } + if (fexclude || finclude) + ok &= file_matching(p->fts_path); + + if (ok) + c += procfile(p->fts_path); + break; + } + } + + fts_close(fts); + return (c); +} + +/* + * Opens a file and processes it. Each file is processed line-by-line + * passing the lines to procline(). + */ +int +procfile(const char *fn) +{ + struct file *f; + struct stat sb; + struct str ln; + mode_t s; + int c, t; + + if (mflag && (mcount <= 0)) + return (0); + + if (strcmp(fn, "-") == 0) { + fn = label != NULL ? label : getstr(1); + f = grep_open(NULL); + } else { + if (!stat(fn, &sb)) { + /* Check if we need to process the file */ + s = sb.st_mode & S_IFMT; + if (s == S_IFDIR && dirbehave == DIR_SKIP) + return (0); + if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK + || s == S_IFSOCK) && devbehave == DEV_SKIP) + return (0); + } + f = grep_open(fn); + } + if (f == NULL) { + if (!sflag) + warn("%s", fn); + if (errno == ENOENT) + notfound = true; + return (0); + } + + ln.file = grep_malloc(strlen(fn) + 1); + strcpy(ln.file, fn); + ln.line_no = 0; + ln.len = 0; + tail = 0; + ln.off = -1; + + for (first = true, c = 0; c == 0 || !(lflag || qflag); ) { + ln.off += ln.len + 1; + if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0) + break; + if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep) + --ln.len; + ln.line_no++; + + /* Return if we need to skip a binary file */ + if (f->binary && binbehave == BINFILE_SKIP) { + grep_close(f); + free(ln.file); + free(f); + return (0); + } + /* Process the file line-by-line */ + t = procline(&ln, f->binary); + c += t; + + /* Count the matches if we have a match limit */ + if (mflag) { + mcount -= t; + if (mcount <= 0) + break; + } + } + if (Bflag > 0) + clearqueue(); + grep_close(f); + + if (cflag) { + if (!hflag) + printf("%s:", ln.file); + printf("%u%c", c, line_sep); + } + if (lflag && !qflag && c != 0) + printf("%s%c", fn, line_sep); + if (Lflag && !qflag && c == 0) + printf("%s%c", fn, line_sep); + if (c && !cflag && !lflag && !Lflag && + binbehave == BINFILE_BIN && f->binary && !qflag) + printf(getstr(8), fn); + + free(ln.file); + free(f); + return (c); +} + +#define iswword(x) (iswalnum((x)) || (x) == L'_') + +/* + * Processes a line comparing it with the specified patterns. Each pattern + * is looped to be compared along with the full string, saving each and every + * match, which is necessary to colorize the output and to count the + * matches. The matching lines are passed to printline() to display the + * appropriate output. + */ +static int +procline(struct str *l, int nottext) +{ + regmatch_t matches[MAX_LINE_MATCHES]; + regmatch_t pmatch; + size_t st = 0; + unsigned int i; + int c = 0, m = 0, r = 0; + + /* Loop to process the whole line */ + while (st <= l->len) { + pmatch.rm_so = st; + pmatch.rm_eo = l->len; + + /* Loop to compare with all the patterns */ + for (i = 0; i < patterns; i++) { +/* + * XXX: grep_search() is a workaround for speed up and should be + * removed in the future. See fastgrep.c. + */ + if (fg_pattern[i].pattern) { + r = grep_search(&fg_pattern[i], + (unsigned char *)l->dat, + l->len, &pmatch); + r = (r == 0) ? 0 : REG_NOMATCH; + st = pmatch.rm_eo; + } else { + r = regexec(&r_pattern[i], l->dat, 1, + &pmatch, eflags); + r = (r == 0) ? 0 : REG_NOMATCH; + st = pmatch.rm_eo; + } + if (r == REG_NOMATCH) + continue; + /* Check for full match */ + if (xflag && + (pmatch.rm_so != 0 || + (size_t)pmatch.rm_eo != l->len)) + continue; + /* Check for whole word match */ + if (fg_pattern[i].word && pmatch.rm_so != 0) { + wint_t wbegin, wend; + + wbegin = wend = L' '; + if (pmatch.rm_so != 0 && + sscanf(&l->dat[pmatch.rm_so - 1], + "%lc", &wbegin) != 1) + continue; + if ((size_t)pmatch.rm_eo != l->len && + sscanf(&l->dat[pmatch.rm_eo], + "%lc", &wend) != 1) + continue; + if (iswword(wbegin) || iswword(wend)) + continue; + } + c = 1; + if (m < MAX_LINE_MATCHES) + matches[m++] = pmatch; + /* matches - skip further patterns */ + if ((color != NULL && !oflag) || qflag || lflag) + break; + } + + if (vflag) { + c = !c; + break; + } + /* One pass if we are not recording matches */ + if ((color != NULL && !oflag) || qflag || lflag) + break; + + if (st == (size_t)pmatch.rm_so) + break; /* No matches */ + } + + if (c && binbehave == BINFILE_BIN && nottext) + return (c); /* Binary file */ + + /* Dealing with the context */ + if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) { + if (c) { + if ((Aflag || Bflag) && !first_global && + (first || since_printed > Bflag)) + printf("--\n"); + tail = Aflag; + if (Bflag > 0) + printqueue(); + printline(l, ':', matches, m); + } else { + printline(l, '-', matches, m); + tail--; + } + first = false; + first_global = false; + since_printed = 0; + } else { + if (Bflag) + enqueue(l); + since_printed++; + } + return (c); +} + +/* + * Safe malloc() for internal use. + */ +void * +grep_malloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc(size)) == NULL) + err(2, "malloc"); + return (ptr); +} + +/* + * Safe calloc() for internal use. + */ +void * +grep_calloc(size_t nmemb, size_t size) +{ + void *ptr; + + if ((ptr = calloc(nmemb, size)) == NULL) + err(2, "calloc"); + return (ptr); +} + +/* + * Safe realloc() for internal use. + */ +void * +grep_realloc(void *ptr, size_t size) +{ + + if ((ptr = realloc(ptr, size)) == NULL) + err(2, "realloc"); + return (ptr); +} + +/* + * Safe strdup() for internal use. + */ +char * +grep_strdup(const char *str) +{ + char *ret; + + if ((ret = strdup(str)) == NULL) + err(2, "strdup"); + return (ret); +} + +/* + * Prints a matching line according to the command line options. + */ +void +printline(struct str *line, int sep, regmatch_t *matches, int m) +{ + size_t a = 0; + int i, n = 0; + + if (!hflag) { + if (nullflag == 0) + fputs(line->file, stdout); + else { + printf("%s", line->file); + putchar(0); + } + ++n; + } + if (nflag) { + if (n > 0) + putchar(sep); + printf("%d", line->line_no); + ++n; + } + if (bflag) { + if (n > 0) + putchar(sep); + printf("%lld", (long long)line->off); + ++n; + } + if (n) + putchar(sep); + /* --color and -o */ + if ((oflag || color) && m > 0) { + for (i = 0; i < m; i++) { + if (!oflag) + fwrite(line->dat + a, matches[i].rm_so - a, 1, + stdout); + if (color) + fprintf(stdout, "\33[%sm\33[K", color); + + fwrite(line->dat + matches[i].rm_so, + matches[i].rm_eo - matches[i].rm_so, 1, + stdout); + if (color) + fprintf(stdout, "\33[m\33[K"); + a = matches[i].rm_eo; + if (oflag) + putchar('\n'); + } + if (!oflag) { + if (line->len - a > 0) + fwrite(line->dat + a, line->len - a, 1, stdout); + putchar(line_sep); + } + } else { + fwrite(line->dat, line->len, 1, stdout); + putchar(line_sep); + } +} 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/id.c b/toolbox/id.c index bc79288..8ec79c1 100644 --- a/toolbox/id.c +++ b/toolbox/id.c @@ -4,10 +4,7 @@ #include <sys/types.h> #include <pwd.h> #include <grp.h> - -#ifdef HAVE_SELINUX #include <selinux/selinux.h> -#endif static void print_uid(uid_t uid) { @@ -34,9 +31,7 @@ int id_main(int argc, char **argv) { gid_t list[64]; int n, max; -#ifdef HAVE_SELINUX char *secctx; -#endif max = getgroups(64, list); if (max < 0) max = 0; @@ -53,12 +48,10 @@ int id_main(int argc, char **argv) print_gid(list[n]); } } -#ifdef HAVE_SELINUX if (getcon(&secctx) == 0) { printf(" context=%s", secctx); free(secctx); } -#endif printf("\n"); return 0; } 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/ls.c b/toolbox/ls.c index a4db99c..5324511 100644 --- a/toolbox/ls.c +++ b/toolbox/ls.c @@ -5,9 +5,7 @@ #include <dirent.h> #include <errno.h> -#ifdef HAVE_SELINUX #include <selinux/selinux.h> -#endif #include <sys/stat.h> #include <unistd.h> @@ -30,6 +28,7 @@ #define LIST_LONG_NUMERIC (1 << 5) #define LIST_CLASSIFY (1 << 6) #define LIST_MACLABEL (1 << 7) +#define LIST_INODE (1 << 8) // fwd static int listpath(const char *name, int flags); @@ -129,22 +128,20 @@ static int show_total_size(const char *dirname, DIR *d, int flags) return 0; } -static int listfile_size(const char *path, const char *filename, int flags) +static int listfile_size(const char *path, const char *filename, struct stat *s, + int flags) { - struct stat s; - - if (lstat(path, &s) < 0) { - fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno)); + if(!s || !path) { return -1; } /* blocks are 512 bytes, we want output to be KB */ if ((flags & LIST_SIZE) != 0) { - printf("%lld ", s.st_blocks / 2); + printf("%lld ", s->st_blocks / 2); } if ((flags & LIST_CLASSIFY) != 0) { - char filetype = mode2kind(s.st_mode); + char filetype = mode2kind(s->st_mode); if (filetype != 'l') { printf("%c ", filetype); } else { @@ -163,15 +160,18 @@ static int listfile_size(const char *path, const char *filename, int flags) return 0; } -static int listfile_long(const char *path, int flags) +static int listfile_long(const char *path, struct stat *s, int flags) { - struct stat s; char date[32]; char mode[16]; char user[16]; char group[16]; const char *name; + if(!s || !path) { + return -1; + } + /* name is anything after the final '/', or the whole path if none*/ name = strrchr(path, '/'); if(name == 0) { @@ -180,36 +180,32 @@ static int listfile_long(const char *path, int flags) name++; } - if(lstat(path, &s) < 0) { - return -1; - } - - mode2str(s.st_mode, mode); + mode2str(s->st_mode, mode); if (flags & LIST_LONG_NUMERIC) { - sprintf(user, "%ld", s.st_uid); - sprintf(group, "%ld", s.st_gid); + sprintf(user, "%ld", s->st_uid); + sprintf(group, "%ld", s->st_gid); } else { - user2str(s.st_uid, user); - group2str(s.st_gid, group); + user2str(s->st_uid, user); + group2str(s->st_gid, group); } - strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime)); + strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime)); date[31] = 0; // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 // MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK) - switch(s.st_mode & S_IFMT) { + switch(s->st_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: printf("%s %-8s %-8s %3d, %3d %s %s\n", mode, user, group, - (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev), + (int) MAJOR(s->st_rdev), (int) MINOR(s->st_rdev), date, name); break; case S_IFREG: printf("%s %-8s %-8s %8lld %s %s\n", - mode, user, group, s.st_size, date, name); + mode, user, group, s->st_size, date, name); break; case S_IFLNK: { char linkto[256]; @@ -239,15 +235,18 @@ static int listfile_long(const char *path, int flags) return 0; } -static int listfile_maclabel(const char *path, int flags) +static int listfile_maclabel(const char *path, struct stat *s, int flags) { - struct stat s; char mode[16]; char user[16]; char group[16]; char *maclabel = NULL; const char *name; + if(!s || !path) { + return -1; + } + /* name is anything after the final '/', or the whole path if none*/ name = strrchr(path, '/'); if(name == 0) { @@ -256,32 +255,24 @@ static int listfile_maclabel(const char *path, int flags) name++; } - if(lstat(path, &s) < 0) { - return -1; - } - -#ifdef HAVE_SELINUX lgetfilecon(path, &maclabel); -#else - maclabel = strdup("-"); -#endif if (!maclabel) { return -1; } - mode2str(s.st_mode, mode); - user2str(s.st_uid, user); - group2str(s.st_gid, group); + mode2str(s->st_mode, mode); + user2str(s->st_uid, user); + group2str(s->st_gid, group); - switch(s.st_mode & S_IFMT) { + switch(s->st_mode & S_IFMT) { case S_IFLNK: { char linkto[256]; - int len; + ssize_t len; len = readlink(path, linkto, sizeof(linkto)); if(len < 0) return -1; - if(len > sizeof(linkto)-1) { + if((size_t)len > sizeof(linkto)-1) { linkto[sizeof(linkto)-4] = '.'; linkto[sizeof(linkto)-3] = '.'; linkto[sizeof(linkto)-2] = '.'; @@ -307,7 +298,9 @@ static int listfile_maclabel(const char *path, int flags) static int listfile(const char *dirname, const char *filename, int flags) { - if ((flags & LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL) == 0) { + struct stat s; + + if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) { printf("%s\n", filename); return 0; } @@ -322,12 +315,20 @@ static int listfile(const char *dirname, const char *filename, int flags) pathname = filename; } + if(lstat(pathname, &s) < 0) { + return -1; + } + + if(flags & LIST_INODE) { + printf("%8llu ", s.st_ino); + } + if ((flags & LIST_MACLABEL) != 0) { - return listfile_maclabel(pathname, flags); + return listfile_maclabel(pathname, &s, flags); } else if ((flags & LIST_LONG) != 0) { - return listfile_long(pathname, flags); + return listfile_long(pathname, &s, flags); } else /*((flags & LIST_SIZE) != 0)*/ { - return listfile_size(pathname, filename, flags); + return listfile_size(pathname, filename, &s, flags); } } @@ -462,6 +463,7 @@ int ls_main(int argc, char **argv) case 'Z': flags |= LIST_MACLABEL; break; case 'a': flags |= LIST_ALL; break; case 'F': flags |= LIST_CLASSIFY; break; + case 'i': flags |= LIST_INODE; break; default: fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]); exit(1); 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/mount.c b/toolbox/mount.c index 27cf3c9..bcda2a2 100644 --- a/toolbox/mount.c +++ b/toolbox/mount.c @@ -19,7 +19,7 @@ #define LOOPDEV_MAXLEN 64 struct mount_opts { - const char str[8]; + const char str[16]; unsigned long rwmask; unsigned long rwset; unsigned long rwnoset; @@ -49,21 +49,27 @@ static const struct mount_opts options[] = { { "exec", MS_NOEXEC, 0, MS_NOEXEC }, { "move", MS_TYPE, MS_MOVE, 0 }, { "recurse", MS_REC, MS_REC, 0 }, + { "rec", MS_REC, MS_REC, 0 }, { "remount", MS_TYPE, MS_REMOUNT, 0 }, { "ro", MS_RDONLY, MS_RDONLY, 0 }, { "rw", MS_RDONLY, 0, MS_RDONLY }, { "suid", MS_NOSUID, 0, MS_NOSUID }, { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0 }, { "verbose", MS_VERBOSE, MS_VERBOSE, 0 }, + { "unbindable", MS_UNBINDABLE, MS_UNBINDABLE, 0 }, + { "private", MS_PRIVATE, MS_PRIVATE, 0 }, + { "slave", MS_SLAVE, MS_SLAVE, 0 }, + { "shared", MS_SHARED, MS_SHARED, 0 }, }; static void add_extra_option(struct extra_opts *extra, char *s) { int len = strlen(s); - int newlen = extra->used_size + len; + int newlen; if (extra->str) len++; /* +1 for ',' */ + newlen = extra->used_size + len; if (newlen >= extra->alloc_size) { char *new; @@ -74,7 +80,7 @@ static void add_extra_option(struct extra_opts *extra, char *s) extra->str = new; extra->end = extra->str + extra->used_size; - extra->alloc_size = newlen; + extra->alloc_size = newlen + 1; } if (extra->used_size) { diff --git a/toolbox/netstat.c b/toolbox/netstat.c index 5768599..05dc640 100644 --- a/toolbox/netstat.c +++ b/toolbox/netstat.c @@ -108,7 +108,7 @@ static void ipv4(const char *filename, const char *label) { addr2str(AF_INET, &raddr, rport, rip); printf("%4s %6d %6d %-22s %-22s %s\n", - label, txq, rxq, lip, rip, + label, rxq, txq, lip, rip, state2str(state)); } } @@ -136,7 +136,7 @@ static void ipv6(const char *filename, const char *label) { addr2str(AF_INET6, &raddr6, rport, rip); printf("%4s %6d %6d %-22s %-22s %s\n", - label, txq, rxq, lip, rip, + label, rxq, txq, lip, rip, state2str(state)); } } 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/restorecon.c b/toolbox/restorecon.c index 5ef0ef1..f9f604f 100644 --- a/toolbox/restorecon.c +++ b/toolbox/restorecon.c @@ -7,8 +7,7 @@ #include <fts.h> #include <selinux/selinux.h> #include <selinux/label.h> - -#define FCPATH "/file_contexts" +#include <selinux/android.h> static struct selabel_handle *sehandle; static const char *progname; @@ -17,7 +16,7 @@ static int verbose; static void usage(void) { - fprintf(stderr, "usage: %s [-f file_contexts] [-nrRv] pathname...\n", progname); + fprintf(stderr, "usage: %s [-nrRv] pathname...\n", progname); exit(1); } @@ -54,21 +53,16 @@ static int restore(const char *pathname, const struct stat *sb) int restorecon_main(int argc, char **argv) { - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, FCPATH } - }; int ch, recurse = 0, ftsflags = FTS_PHYSICAL; + int i = 0; progname = argv[0]; do { - ch = getopt(argc, argv, "f:nrRv"); + ch = getopt(argc, argv, "nrRv"); if (ch == EOF) break; switch (ch) { - case 'f': - seopts[0].value = optarg; - break; case 'n': nochange = 1; break; @@ -89,9 +83,10 @@ int restorecon_main(int argc, char **argv) if (!argc) usage(); - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); + sehandle = selinux_android_file_context_handle(); + if (!sehandle) { - fprintf(stderr, "Could not load file contexts from %s: %s\n", seopts[0].value, + fprintf(stderr, "Could not load file_contexts: %s\n", strerror(errno)); return -1; } diff --git a/toolbox/rm.c b/toolbox/rm.c index bd66311..3a24bec 100644 --- a/toolbox/rm.c +++ b/toolbox/rm.c @@ -7,14 +7,17 @@ #include <sys/stat.h> #include <sys/types.h> +#define OPT_RECURSIVE 1 +#define OPT_FORCE 2 + static int usage() { - fprintf(stderr,"rm [-rR] <target>\n"); + fprintf(stderr,"Usage: rm [-rR] [-f] <target>\n"); return -1; } /* return -1 on failure, with errno set to the first error */ -static int unlink_recursive(const char* name) +static int unlink_recursive(const char* name, int flags) { struct stat st; DIR *dir; @@ -23,7 +26,7 @@ static int unlink_recursive(const char* name) /* is it a file or directory? */ if (lstat(name, &st) < 0) - return -1; + return ((flags & OPT_FORCE) && errno == ENOENT) ? 0 : -1; /* a file, so unlink it */ if (!S_ISDIR(st.st_mode)) @@ -41,7 +44,7 @@ static int unlink_recursive(const char* name) if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) continue; sprintf(dn, "%s/%s", name, de->d_name); - if (unlink_recursive(dn) < 0) { + if (unlink_recursive(dn, flags) < 0) { fail = 1; break; } @@ -66,21 +69,45 @@ static int unlink_recursive(const char* name) int rm_main(int argc, char *argv[]) { int ret; - int i = 1; - int recursive = 0; + int i, c; + int flags = 0; if (argc < 2) return usage(); - /* check if recursive */ - if (argc >=2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "-R"))) { - recursive = 1; - i = 2; + /* check flags */ + do { + c = getopt(argc, argv, "frR"); + if (c == EOF) + break; + switch (c) { + case 'f': + flags |= OPT_FORCE; + break; + case 'r': + case 'R': + flags |= OPT_RECURSIVE; + break; + } + } while (1); + + if (optind < 1 || optind >= argc) { + usage(); + return -1; } - + /* loop over the file/directory args */ - for (; i < argc; i++) { - int ret = recursive ? unlink_recursive(argv[i]) : unlink(argv[i]); + for (i = optind; i < argc; i++) { + + if (flags & OPT_RECURSIVE) { + ret = unlink_recursive(argv[i], flags); + } else { + ret = unlink(argv[i]); + if (errno == ENOENT && (flags & OPT_FORCE)) { + return 0; + } + } + if (ret < 0) { fprintf(stderr, "rm failed for %s, %s\n", argv[i], strerror(errno)); return -1; 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/setenforce.c b/toolbox/setenforce.c index 1b0ea5c..444073d 100644 --- a/toolbox/setenforce.c +++ b/toolbox/setenforce.c @@ -7,7 +7,7 @@ #include <errno.h> #include <selinux/selinux.h> -void usage(const char *progname) +static void usage(const char *progname) { fprintf(stderr, "usage: %s [ Enforcing | Permissive | 1 | 0 ]\n", progname); diff --git a/toolbox/setsebool.c b/toolbox/setsebool.c index 4a3d87d..f79a612 100644 --- a/toolbox/setsebool.c +++ b/toolbox/setsebool.c @@ -9,35 +9,26 @@ #include <errno.h> static int do_setsebool(int nargs, char **args) { - SELboolean *b = alloca(nargs * sizeof(SELboolean)); - char *v; - int i; + const char *name = args[1]; + const char *value = args[2]; + SELboolean b; if (is_selinux_enabled() <= 0) return 0; - for (i = 1; i < nargs; i++) { - char *name = args[i]; - v = strchr(name, '='); - if (!v) { - fprintf(stderr, "setsebool: argument %s had no =\n", name); - return -1; - } - *v++ = 0; - b[i-1].name = name; - if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on")) - b[i-1].value = 1; - else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off")) - b[i-1].value = 0; - else { - fprintf(stderr, "setsebool: invalid value %s\n", v); - return -1; - } + b.name = name; + if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on")) + b.value = 1; + else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off")) + b.value = 0; + else { + fprintf(stderr, "setsebool: invalid value %s\n", value); + return -1; } - if (security_set_boolean_list(nargs - 1, b, 0) < 0) + if (security_set_boolean_list(1, &b, 0) < 0) { - fprintf(stderr, "setsebool: unable to set booleans: %s", strerror(errno)); + fprintf(stderr, "setsebool: could not set %s to %s: %s", name, value, strerror(errno)); return -1; } @@ -46,8 +37,8 @@ static int do_setsebool(int nargs, char **args) { int setsebool_main(int argc, char **argv) { - if (argc < 2) { - fprintf(stderr, "Usage: %s name=value...\n", argv[0]); + if (argc != 3) { + fprintf(stderr, "Usage: %s name value\n", argv[0]); exit(1); } 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'; } } diff --git a/toolbox/vmstat.c b/toolbox/vmstat.c index 600f136..4086ed0 100644 --- a/toolbox/vmstat.c +++ b/toolbox/vmstat.c @@ -75,7 +75,7 @@ int vmstat_main(int argc, char *argv[]) { int toggle, count; int i; - iterations = 0; + iterations = -1; delay = 1; header_interval = 20; @@ -119,7 +119,7 @@ int vmstat_main(int argc, char *argv[]) { if (!header_interval) print_header(); read_state(&s[1 - toggle]); - while ((iterations == 0) || (iterations-- > 0)) { + while ((iterations < 0) || (iterations-- > 0)) { sleep(delay); read_state(&s[toggle]); if (header_interval) { diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c index d311992..bf82882 100644 --- a/toolbox/watchprops.c +++ b/toolbox/watchprops.c @@ -1,35 +1,30 @@ #include <stdio.h> #include <stdlib.h> #include <time.h> +#include <errno.h> #include <cutils/properties.h> +#include <cutils/hashmap.h> #include <sys/atomics.h> #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include <sys/_system_properties.h> - -extern prop_area *__system_property_area__; - -typedef struct pwatch pwatch; - -struct pwatch +static int str_hash(void *key) { - const prop_info *pi; - unsigned serial; -}; + return hashmapHash(key, strlen(key)); +} -static pwatch watchlist[1024]; +static bool str_equals(void *keyA, void *keyB) +{ + return strcmp(keyA, keyB) == 0; +} -static void announce(const prop_info *pi) +static void announce(char *name, char *value) { - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; char *x; - __system_property_read(pi, name, value); - for(x = value; *x; x++) { if((*x < 32) || (*x > 127)) *x = '.'; } @@ -37,40 +32,64 @@ static void announce(const prop_info *pi) fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value); } +static void add_to_watchlist(Hashmap *watchlist, const char *name, + const prop_info *pi) +{ + char *key = strdup(name); + unsigned *value = malloc(sizeof(unsigned)); + if (!key || !value) + exit(1); + + *value = __system_property_serial(pi); + hashmapPut(watchlist, key, value); +} + +static void populate_watchlist(const prop_info *pi, void *cookie) +{ + Hashmap *watchlist = cookie; + char name[PROP_NAME_MAX]; + char value_unused[PROP_VALUE_MAX]; + + __system_property_read(pi, name, value_unused); + add_to_watchlist(watchlist, name, pi); +} + +static void update_watchlist(const prop_info *pi, void *cookie) +{ + Hashmap *watchlist = cookie; + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; + unsigned *serial; + + __system_property_read(pi, name, value); + serial = hashmapGet(watchlist, name); + if (!serial) { + add_to_watchlist(watchlist, name, pi); + announce(name, value); + } else { + unsigned tmp = __system_property_serial(pi); + if (*serial != tmp) { + *serial = tmp; + announce(name, value); + } + } +} + int watchprops_main(int argc, char *argv[]) { - prop_area *pa = __system_property_area__; - unsigned serial = pa->serial; - unsigned count = pa->count; + unsigned serial = 0; + unsigned count = 0; unsigned n; - if(count >= 1024) exit(1); + Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals); + if (!watchlist) + exit(1); - for(n = 0; n < count; n++) { - watchlist[n].pi = __system_property_find_nth(n); - watchlist[n].serial = watchlist[n].pi->serial; - } + __system_property_foreach(populate_watchlist, watchlist); for(;;) { - do { - __futex_wait(&pa->serial, serial, 0); - } while(pa->serial == serial); - - while(count < pa->count){ - watchlist[count].pi = __system_property_find_nth(count); - watchlist[count].serial = watchlist[n].pi->serial; - announce(watchlist[count].pi); - count++; - if(count == 1024) exit(1); - } - - for(n = 0; n < count; n++){ - unsigned tmp = watchlist[n].pi->serial; - if(watchlist[n].serial != tmp) { - announce(watchlist[n].pi); - watchlist[n].serial = tmp; - } - } + serial = __system_property_wait_any(serial); + __system_property_foreach(update_watchlist, watchlist); } return 0; } |
