summaryrefslogtreecommitdiffstats
path: root/libcutils
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53 (patch)
tree54fd1b2695a591d2306d41264df67c53077b752c /libcutils
downloadsystem_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.zip
system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.gz
system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.bz2
Initial Contribution
Diffstat (limited to 'libcutils')
-rw-r--r--libcutils/Android.mk106
-rw-r--r--libcutils/MODULE_LICENSE_APACHE20
-rw-r--r--libcutils/NOTICE190
-rw-r--r--libcutils/adb_networking.c172
-rw-r--r--libcutils/array.c155
-rw-r--r--libcutils/ashmem-dev.c85
-rw-r--r--libcutils/ashmem-host.c94
-rw-r--r--libcutils/atomic-android-arm.S222
-rw-r--r--libcutils/atomic-android-armv6.S169
-rw-r--r--libcutils/atomic.c335
-rw-r--r--libcutils/buffer.c116
-rw-r--r--libcutils/buffer.h112
-rw-r--r--libcutils/config_utils.c317
-rw-r--r--libcutils/cpu_info.c83
-rw-r--r--libcutils/dir_hash.c334
-rw-r--r--libcutils/dlmalloc_stubs.c29
-rw-r--r--libcutils/fdevent.c506
-rw-r--r--libcutils/hashmap.c350
-rw-r--r--libcutils/load_file.c51
-rw-r--r--libcutils/loghack.h38
-rw-r--r--libcutils/memory.c87
-rw-r--r--libcutils/memset32.S93
-rw-r--r--libcutils/mq.c1357
-rw-r--r--libcutils/mspace.c246
-rw-r--r--libcutils/private.h368
-rw-r--r--libcutils/process_name.c75
-rw-r--r--libcutils/properties.c368
-rw-r--r--libcutils/record_stream.c186
-rw-r--r--libcutils/selector.c263
-rw-r--r--libcutils/socket_inaddr_any_server.c70
-rw-r--r--libcutils/socket_local.h39
-rw-r--r--libcutils/socket_local_client.c167
-rw-r--r--libcutils/socket_local_server.c124
-rw-r--r--libcutils/socket_loopback_client.c59
-rw-r--r--libcutils/socket_loopback_server.c71
-rw-r--r--libcutils/socket_network_client.c65
-rw-r--r--libcutils/strdup16to8.c104
-rw-r--r--libcutils/strdup8to16.c209
-rw-r--r--libcutils/threads.c84
-rw-r--r--libcutils/tzfile.h180
-rw-r--r--libcutils/tztime.c1915
-rw-r--r--libcutils/uio.c76
-rw-r--r--libcutils/zygote.c267
43 files changed, 9937 insertions, 0 deletions
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
new file mode 100644
index 0000000..3d0c12b
--- /dev/null
+++ b/libcutils/Android.mk
@@ -0,0 +1,106 @@
+#
+# 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.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+commonSources := \
+ array.c \
+ hashmap.c \
+ atomic.c \
+ buffer.c \
+ socket_inaddr_any_server.c \
+ socket_local_client.c \
+ socket_local_server.c \
+ socket_loopback_client.c \
+ socket_loopback_server.c \
+ socket_network_client.c \
+ config_utils.c \
+ cpu_info.c \
+ load_file.c \
+ strdup16to8.c \
+ strdup8to16.c \
+ record_stream.c \
+ process_name.c \
+ properties.c \
+ threads.c
+
+# some files must not be compiled when building against Mingw
+# they correspond to features not used by our host development tools
+# which are also hard or even impossible to port to native Win32
+WITH_MINGW :=
+ifeq ($(HOST_OS),windows)
+ ifeq ($(strip $(USE_CYGWIN)),)
+ WITH_MINGW := 1
+ endif
+endif
+# USE_MINGW is defined when we build against Mingw on Linux
+ifneq ($(strip $(USE_MINGW)),)
+ WITH_MINGW := 1
+endif
+
+ifeq ($(WITH_MINGW),1)
+ commonSources += \
+ uio.c
+else
+ commonSources += \
+ mspace.c \
+ selector.c \
+ fdevent.c \
+ tztime.c \
+ adb_networking.c \
+ zygote.c
+endif
+
+
+# Static library for host
+# ========================================================
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) ashmem-host.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+ifeq ($(TARGET_SIMULATOR),true)
+
+# Shared library for simulator
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) memory.c dlmalloc_stubs.c ashmem-host.c
+LOCAL_LDLIBS := -lpthread
+LOCAL_SHARED_LIBRARIES := liblog
+include $(BUILD_SHARED_LIBRARY)
+
+else #!sim
+
+# Shared and static library for target
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_SRC_FILES := $(commonSources) memset32.S atomic-android-arm.S mq.c \
+ ashmem-dev.c
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_STATIC_LIBRARIES := liblog
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcutils
+LOCAL_WHOLE_STATIC_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := liblog
+include $(BUILD_SHARED_LIBRARY)
+
+endif #!sim
diff --git a/libcutils/MODULE_LICENSE_APACHE2 b/libcutils/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libcutils/MODULE_LICENSE_APACHE2
diff --git a/libcutils/NOTICE b/libcutils/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libcutils/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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/libcutils/adb_networking.c b/libcutils/adb_networking.c
new file mode 100644
index 0000000..d819d44
--- /dev/null
+++ b/libcutils/adb_networking.c
@@ -0,0 +1,172 @@
+/* libs/utils/adb_networking.c
+**
+** 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 ADB_PORT 5037
+
+#define _GNU_SOURCE /* for asprintf */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <cutils/adb_networking.h>
+#include <cutils/sockets.h>
+#include <cutils/properties.h>
+
+#define ADB_RESPONSE_SIZE 4
+
+/**
+ * Unfortunately, java.net.Socket wants to create it's filedescriptor early
+ * So, this function takes an fd that must be an unconnected
+ * PF_LOCAL SOCK_STREAM
+ */
+int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address)
+{
+ struct sockaddr_in local_addr;
+ socklen_t alen;
+ char *cmd;
+ char buf[ADB_RESPONSE_SIZE + 1];
+ ssize_t count_read;
+ int ret;
+ int err;
+ /* for impl of inet_ntoa below*/
+ union {
+ uint8_t b[4];
+ uint32_t l;
+ } a;
+
+ /* First, connect to adb */
+
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr.sin_family = AF_INET;
+ local_addr.sin_port = htons(ADB_PORT);
+ local_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ do {
+ err = connect(fd, (struct sockaddr *) &local_addr, sizeof(local_addr));
+ } while (err < 0 && errno == EINTR);
+
+ if (err < 0) {
+ return -1;
+ }
+
+ a.l = p_address->sin_addr.s_addr;
+
+ // compose the command
+ asprintf(&cmd, "tcp:%u:%u.%u.%u.%u",
+ (unsigned int)ntohs(p_address->sin_port),
+ a.b[0],a.b[1],a.b[2],a.b[3]);
+
+ // buf is now the ascii hex length of cmd
+ snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
+
+ // write the 4-byte length
+ do {
+ err = write(fd, buf, 4);
+ } while (err < 0 && errno == EINTR);
+
+ // write the command
+ do {
+ err = write(fd, cmd, strlen(cmd));
+ } while (err < 0 && errno == EINTR);
+
+ // read the result
+ do {
+ count_read = read(fd, buf, sizeof(buf) - 1);
+ } while (count_read < 0 && errno != EINTR);
+
+ if (count_read == ADB_RESPONSE_SIZE
+ && 0 == strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
+ ret = 0;
+ } else {
+ /* what errno here? <shrug? */
+ errno = ENETUNREACH;
+ ret = -1;
+ }
+
+ free(cmd);
+
+ return ret;
+}
+
+/**
+ * Fills in *p_out_addr and returns 0 on success
+ * Memset's *p_out_addr and returns -1 on fail
+ */
+
+int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr)
+{
+ int fd;
+ char *cmd = NULL;
+ char buf[ADB_RESPONSE_SIZE + 1];
+ int err;
+ ssize_t count_read;
+
+ fd = socket_loopback_client(ADB_PORT, SOCK_STREAM);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ // compose the command
+ asprintf(&cmd, "dns:%s", name);
+
+ // buf is now the ascii hex length of cmd
+ snprintf(buf, sizeof(buf), "%04X", strlen(cmd));
+
+ // write the 4-byte length
+ do {
+ err = write(fd, buf, 4);
+ } while (err < 0 && errno == EINTR);
+
+ // write the command
+ do {
+ err = write(fd, cmd, strlen(cmd));
+ } while (err < 0 && errno == EINTR);
+
+ // read the result
+ do {
+ count_read = read(fd, buf, ADB_RESPONSE_SIZE);
+ } while (count_read < 0 && errno != EINTR);
+
+ if (count_read != ADB_RESPONSE_SIZE
+ || 0 != strncmp(buf, "OKAY", ADB_RESPONSE_SIZE)) {
+ goto error;
+ }
+
+ // read the actual IP address
+ do {
+ count_read = read(fd, &(p_out_addr->s_addr), sizeof(p_out_addr->s_addr));
+ } while (count_read < 0 && errno != EINTR);
+
+ if (count_read != 4) {
+ goto error;
+ }
+
+ free(cmd);
+ close(fd);
+ return 0;
+error:
+ free(cmd);
+ close(fd);
+ memset(p_out_addr, 0, sizeof(struct in_addr));
+ return -1;
+}
+
diff --git a/libcutils/array.c b/libcutils/array.c
new file mode 100644
index 0000000..ff2c8ff
--- /dev/null
+++ b/libcutils/array.c
@@ -0,0 +1,155 @@
+/*
+ * 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/array.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define INITIAL_CAPACITY (4)
+
+struct Array {
+ void** contents;
+ int size;
+ int capacity;
+};
+
+Array* arrayCreate() {
+ return calloc(1, sizeof(struct Array));
+}
+
+void arrayFree(Array* array) {
+ assert(array != NULL);
+
+ // Free internal array.
+ free(array->contents);
+
+ // Free the Array itself.
+ free(array);
+}
+
+/** Returns 0 if successful, < 0 otherwise.. */
+static int ensureCapacity(Array* array, int capacity) {
+ int oldCapacity = array->capacity;
+ if (capacity > oldCapacity) {
+ int newCapacity = (oldCapacity == 0) ? INITIAL_CAPACITY : oldCapacity * 2;
+
+ // Keep doubling capacity until we surpass necessary capacity.
+ while (newCapacity < capacity) {
+ newCapacity *= 2;
+ }
+
+ void** newContents;
+ if (array->contents == NULL) {
+ // Allocate new array.
+ newContents = malloc(newCapacity * sizeof(void*));
+ if (newContents == NULL) {
+ return -1;
+ }
+ } else {
+ // Expand existing array.
+ newContents = realloc(array->contents, sizeof(void*) * newCapacity);
+ if (newContents == NULL) {
+ return -1;
+ }
+ }
+
+ array->capacity = newCapacity;
+ array->contents = newContents;
+ }
+
+ return 0;
+}
+
+int arrayAdd(Array* array, void* pointer) {
+ assert(array != NULL);
+ int size = array->size;
+ int result = ensureCapacity(array, size + 1);
+ if (result < 0) {
+ return result;
+ }
+ array->contents[size] = pointer;
+ array->size++;
+ return 0;
+}
+
+static inline void checkBounds(Array* array, int index) {
+ assert(array != NULL);
+ assert(index < array->size);
+ assert(index >= 0);
+}
+
+void* arrayGet(Array* array, int index) {
+ checkBounds(array, index);
+ return array->contents[index];
+}
+
+void* arrayRemove(Array* array, int index) {
+ checkBounds(array, index);
+
+ void* pointer = array->contents[index];
+
+ int newSize = array->size - 1;
+
+ // Shift entries left.
+ if (index != newSize) {
+ memmove(array->contents + index, array->contents + index + 1,
+ (sizeof(void*)) * (newSize - index));
+ }
+
+ array->size = newSize;
+
+ return pointer;
+}
+
+void* arraySet(Array* array, int index, void* pointer) {
+ checkBounds(array, index);
+ void* old = array->contents[index];
+ array->contents[index] = pointer;
+ return old;
+}
+
+int arraySetSize(Array* array, int newSize) {
+ assert(array != NULL);
+ assert(newSize >= 0);
+
+ int oldSize = array->size;
+
+ if (newSize > oldSize) {
+ // Expand.
+ int result = ensureCapacity(array, newSize);
+ if (result < 0) {
+ return result;
+ }
+
+ // Zero out new entries.
+ memset(array->contents + sizeof(void*) * oldSize, 0,
+ sizeof(void*) * (newSize - oldSize));
+ }
+
+ array->size = newSize;
+
+ return 0;
+}
+
+int arraySize(Array* array) {
+ assert(array != NULL);
+ return array->size;
+}
+
+const void** arrayUnwrap(Array* array) {
+ return array->contents;
+}
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
new file mode 100644
index 0000000..5e158af
--- /dev/null
+++ b/libcutils/ashmem-dev.c
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for devices, which have our
+ * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version,
+ * used by the simulator.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <linux/ashmem.h>
+#include <cutils/ashmem.h>
+
+#define ASHMEM_DEVICE "/dev/ashmem"
+
+/*
+ * ashmem_create_region - creates a new ashmem region and returns the file
+ * descriptor, or <0 on error
+ *
+ * `name' is an optional label to give the region (visible in /proc/pid/maps)
+ * `size' is the size of the region, in page-aligned bytes
+ */
+int ashmem_create_region(const char *name, size_t size)
+{
+ int fd, ret;
+
+ fd = open(ASHMEM_DEVICE, O_RDWR);
+ if (fd < 0)
+ return fd;
+
+ if (name) {
+ char buf[ASHMEM_NAME_LEN];
+
+ strlcpy(buf, name, sizeof(buf));
+ ret = ioctl(fd, ASHMEM_SET_NAME, buf);
+ if (ret < 0)
+ goto error;
+ }
+
+ ret = ioctl(fd, ASHMEM_SET_SIZE, size);
+ if (ret < 0)
+ goto error;
+
+ return fd;
+
+error:
+ close(fd);
+ return ret;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+ return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+ struct ashmem_pin pin = { offset, len };
+ return ioctl(fd, ASHMEM_PIN, &pin);
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+ struct ashmem_pin pin = { offset, len };
+ return ioctl(fd, ASHMEM_UNPIN, &pin);
+}
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
new file mode 100644
index 0000000..dbb52bc
--- /dev/null
+++ b/libcutils/ashmem-host.c
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+/*
+ * Implementation of the user-space ashmem API for the simulator, which lacks
+ * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#include <cutils/ashmem.h>
+
+int ashmem_create_region(const char *ignored, size_t size)
+{
+ static const char txt[] = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char name[64];
+ unsigned int retries = 0;
+ pid_t pid = getpid();
+ int fd;
+
+ srand(time(NULL) + pid);
+
+retry:
+ /* not beautiful, its just wolf-like loop unrolling */
+ snprintf(name, sizeof(name), "/tmp/android-ashmem-%d-%c%c%c%c%c%c%c%c",
+ pid,
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))],
+ txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))]);
+
+ /* open O_EXCL & O_CREAT: we are either the sole owner or we fail */
+ fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd == -1) {
+ /* unlikely, but if we failed because `name' exists, retry */
+ if (errno == EEXIST && ++retries < 6)
+ goto retry;
+ return -1;
+ }
+
+ /* truncate the file to `len' bytes */
+ if (ftruncate(fd, size) == -1)
+ goto error;
+
+ if (unlink(name) == -1)
+ goto error;
+
+ return fd;
+error:
+ close(fd);
+ return -1;
+}
+
+int ashmem_set_prot_region(int fd, int prot)
+{
+ return 0;
+}
+
+int ashmem_pin_region(int fd, size_t offset, size_t len)
+{
+ return ASHMEM_NOT_PURGED;
+}
+
+int ashmem_unpin_region(int fd, size_t offset, size_t len)
+{
+ return ASHMEM_IS_UNPINNED;
+}
diff --git a/libcutils/atomic-android-arm.S b/libcutils/atomic-android-arm.S
new file mode 100644
index 0000000..2a4c34f
--- /dev/null
+++ b/libcutils/atomic-android-arm.S
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ */
+
+/*
+ * NOTE: these atomic operations are SMP safe on all architectures,
+ * except swap(), see below.
+ */
+
+ .text
+ .align
+
+ .global android_atomic_write
+
+ .global android_atomic_inc
+ .global android_atomic_dec
+
+ .global android_atomic_add
+ .global android_atomic_and
+ .global android_atomic_or
+
+ .global android_atomic_swap
+
+ .global android_atomic_cmpxchg
+
+/*
+ * ----------------------------------------------------------------------------
+ * int __kernel_cmpxchg(int oldval, int newval, int *ptr)
+ * clobbered: r3, ip, flags
+ * return 0 if a swap was made, non-zero otherwise.
+ */
+
+ .equ kernel_cmpxchg, 0xFFFF0FC0
+ .equ kernel_atomic_base, 0xFFFF0FFF
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_write
+ * input: r0=value, r1=address
+ * output: void
+ */
+
+android_atomic_write:
+ stmdb sp!, {r4, lr}
+ mov r2, r1
+ mov r1, r0
+1: @ android_atomic_write
+ ldr r0, [r2]
+ mov r3, #kernel_atomic_base
+ add lr, pc, #4
+ add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+ bcc 1b
+ ldmia sp!, {r4, lr}
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_inc
+ * input: r0 = address
+ * output: r0 = old value
+ */
+
+android_atomic_inc:
+ stmdb sp!, {r4, lr}
+ mov r2, r0
+1: @ android_atomic_inc
+ ldr r0, [r2]
+ mov r3, #kernel_atomic_base
+ add lr, pc, #4
+ add r1, r0, #1
+ add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+ bcc 1b
+ sub r0, r1, #1
+ ldmia sp!, {r4, lr}
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_dec
+ * input: r0=address
+ * output: r0 = old value
+ */
+
+android_atomic_dec:
+ stmdb sp!, {r4, lr}
+ mov r2, r0
+1: @ android_atomic_dec
+ ldr r0, [r2]
+ mov r3, #kernel_atomic_base
+ add lr, pc, #4
+ sub r1, r0, #1
+ add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+ bcc 1b
+ add r0, r1, #1
+ ldmia sp!, {r4, lr}
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_add
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_add:
+ stmdb sp!, {r4, lr}
+ mov r2, r1
+ mov r4, r0
+1: @ android_atomic_add
+ ldr r0, [r2]
+ mov r3, #kernel_atomic_base
+ add lr, pc, #4
+ add r1, r0, r4
+ add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+ bcc 1b
+ sub r0, r1, r4
+ ldmia sp!, {r4, lr}
+ bx lr
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_and
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_and:
+ stmdb sp!, {r4, r5, lr}
+ mov r2, r1 /* r2 = address */
+ mov r4, r0 /* r4 = the value */
+1: @ android_atomic_and
+ ldr r0, [r2] /* r0 = address[0] */
+ mov r3, #kernel_atomic_base
+ add lr, pc, #8
+ mov r5, r0 /* r5 = save address[0] */
+ and r1, r0, r4 /* r1 = new value */
+ add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */
+ bcc 1b
+ mov r0, r5
+ ldmia sp!, {r4, r5, lr}
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_or
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_or:
+ stmdb sp!, {r4, r5, lr}
+ mov r2, r1 /* r2 = address */
+ mov r4, r0 /* r4 = the value */
+1: @ android_atomic_or
+ ldr r0, [r2] /* r0 = address[0] */
+ mov r3, #kernel_atomic_base
+ add lr, pc, #8
+ mov r5, r0 /* r5 = save address[0] */
+ orr r1, r0, r4 /* r1 = new value */
+ add pc, r3, #(kernel_cmpxchg - kernel_atomic_base) /* call cmpxchg() */
+ bcc 1b
+ mov r0, r5
+ ldmia sp!, {r4, r5, lr}
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_swap
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+/* FIXME: this is not safe on SMP systems
+ * a general way to do it is to use kernel_cmpxchg */
+
+android_atomic_swap:
+ swp r0, r0, [r1]
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg
+ * input: r0=oldvalue, r1=newvalue, r2=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+
+android_atomic_cmpxchg:
+ stmdb sp!, {r4, lr}
+ mov r4, r0 /* r4 = save oldvalue */
+1: @ android_atomic_cmpxchg
+ mov r3, #kernel_atomic_base
+ add lr, pc, #4
+ mov r0, r4 /* r0 = oldvalue */
+ add pc, r3, #(kernel_cmpxchg - kernel_atomic_base)
+ bcs 2f /* swap was made. we're good, return. */
+ ldr r3, [r2] /* swap not made, see if it's because *ptr!=oldvalue */
+ cmp r3, r4
+ beq 1b
+2: @ android_atomic_cmpxchg
+ ldmia sp!, {r4, lr}
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg_64
+ * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */
diff --git a/libcutils/atomic-android-armv6.S b/libcutils/atomic-android-armv6.S
new file mode 100644
index 0000000..64146c1
--- /dev/null
+++ b/libcutils/atomic-android-armv6.S
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+ .text
+ .align
+
+ .global android_atomic_write
+
+ .global android_atomic_inc
+ .global android_atomic_dec
+
+ .global android_atomic_add
+ .global android_atomic_and
+ .global android_atomic_or
+
+ .global android_atomic_swap
+
+ .global android_atomic_cmpxchg
+
+
+
+/* FIXME: On SMP systems memory barriers may be needed */
+#warning "this file is not safe with SMP systems"
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_write
+ * input: r0=value, r1=address
+ * output: void
+ */
+
+android_atomic_write:
+1: ldrex r12, [r1]
+ strex r12, r0, [r1]
+ cmp r12, #0
+ bne 1b
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_inc
+ * input: r0 = address
+ * output: r0 = old value
+ */
+
+android_atomic_inc:
+ mov r12, r0
+1: ldrex r0, [r12]
+ add r2, r0, #1
+ strex r1, r2, [r12]
+ cmp r1, #0
+ bxeq lr
+ b 1b
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_dec
+ * input: r0=address
+ * output: r0 = old value
+ */
+
+android_atomic_dec:
+ mov r12, r0
+1: ldrex r0, [r12]
+ sub r2, r0, #1
+ strex r1, r2, [r12]
+ cmp r1, #0
+ bxeq lr
+ b 1b
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_add
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_add:
+ mov r12, r0
+1: ldrex r0, [r1]
+ add r2, r0, r12
+ strex r3, r2, [r1]
+ cmp r3, #0
+ bxeq lr
+ b 1b
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_and
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_and:
+ mov r12, r0
+1: ldrex r0, [r1]
+ and r2, r0, r12
+ strex r3, r2, [r1]
+ cmp r3, #0
+ bxeq lr
+ b 1b
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_or
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_or:
+ mov r12, r0
+1: ldrex r0, [r1]
+ orr r2, r0, r12
+ strex r3, r2, [r1]
+ cmp r3, #0
+ bxeq lr
+ b 1b
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_swap
+ * input: r0=value, r1=address
+ * output: r0 = old value
+ */
+
+android_atomic_swap:
+ swp r0, r0, [r1]
+ bx lr
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg
+ * input: r0=oldvalue, r1=newvalue, r2=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+
+android_atomic_cmpxchg:
+ mov r12, r1
+ ldrex r3, [r2]
+ eors r0, r0, r3
+ strexeq r0, r12, [r2]
+ bx lr
+
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * android_atomic_cmpxchg_64
+ * input: r0-r1=oldvalue, r2-r3=newvalue, arg4 (on stack)=address
+ * output: r0 = 0 (xchg done) or non-zero (xchg not done)
+ */
+/* TODO: NEED IMPLEMENTATION FOR THIS ARCHITECTURE */
diff --git a/libcutils/atomic.c b/libcutils/atomic.c
new file mode 100644
index 0000000..65d7af0
--- /dev/null
+++ b/libcutils/atomic.c
@@ -0,0 +1,335 @@
+/*
+ * 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
+
+/*****************************************************************************/
+#if defined(HAVE_MACOSX_IPC)
+
+#include <libkern/OSAtomic.h>
+
+void android_atomic_write(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (OSAtomicCompareAndSwap32Barrier(oldValue, value, (int32_t*)addr) == 0);
+}
+
+int32_t android_atomic_inc(volatile int32_t* addr) {
+ return OSAtomicIncrement32Barrier((int32_t*)addr)-1;
+}
+
+int32_t android_atomic_dec(volatile int32_t* addr) {
+ return OSAtomicDecrement32Barrier((int32_t*)addr)+1;
+}
+
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr) {
+ return OSAtomicAdd32Barrier(value, (int32_t*)addr)-value;
+}
+
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue&value, (int32_t*)addr) == 0);
+ return oldValue;
+}
+
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (OSAtomicCompareAndSwap32Barrier(oldValue, oldValue|value, (int32_t*)addr) == 0);
+ return oldValue;
+}
+
+int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, value, addr));
+ return oldValue;
+}
+
+int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) {
+ return OSAtomicCompareAndSwap32Barrier(oldvalue, newvalue, (int32_t*)addr) == 0;
+}
+
+#if defined(__ppc__) \
+ || defined(__PPC__) \
+ || defined(__powerpc__) \
+ || defined(__powerpc) \
+ || defined(__POWERPC__) \
+ || defined(_M_PPC) \
+ || defined(__PPC)
+#define NEED_QUASIATOMICS 1
+#else
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr) {
+ return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
+ (int64_t*)addr) == 0;
+}
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+ int64_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_quasiatomic_cmpxchg_64(oldValue, value, addr));
+ return oldValue;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+ return OSAtomicAdd64Barrier(0, addr);
+}
+
+#endif
+
+
+/*****************************************************************************/
+#elif defined(__i386__) || defined(__x86_64__)
+
+void android_atomic_write(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, value, addr));
+}
+
+int32_t android_atomic_inc(volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_dec(volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, oldValue-1, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_add(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, oldValue+value, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_and(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, oldValue&value, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_or(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, oldValue|value, addr));
+ return oldValue;
+}
+
+int32_t android_atomic_swap(int32_t value, volatile int32_t* addr) {
+ int32_t oldValue;
+ do {
+ oldValue = *addr;
+ } while (android_atomic_cmpxchg(oldValue, value, addr));
+ return oldValue;
+}
+
+int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) {
+ int xchg;
+ asm volatile
+ (
+ " lock; cmpxchg %%ecx, (%%edx);"
+ " setne %%al;"
+ " andl $1, %%eax"
+ : "=a" (xchg)
+ : "a" (oldvalue), "c" (newvalue), "d" (addr)
+ );
+ return xchg;
+}
+
+#define NEED_QUASIATOMICS 1
+
+/*****************************************************************************/
+#elif __arm__
+// Most of the implementation is in atomic-android-arm.s.
+
+// on the device, we implement the 64-bit atomic operations through
+// mutex locking. normally, this is bad because we must initialize
+// a pthread_mutex_t before being able to use it, and this means
+// having to do an initialization check on each function call, and
+// that's where really ugly things begin...
+//
+// BUT, as a special twist, we take advantage of the fact that in our
+// pthread library, a mutex is simply a volatile word whose value is always
+// initialized to 0. In other words, simply declaring a static mutex
+// object initializes it !
+//
+// another twist is that we use a small array of mutexes to dispatch
+// the contention locks from different memory addresses
+//
+
+#include <pthread.h>
+
+#define SWAP_LOCK_COUNT 32U
+static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT];
+
+#define SWAP_LOCK(addr) \
+ &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
+
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+ int64_t oldValue;
+ pthread_mutex_t* lock = SWAP_LOCK(addr);
+
+ pthread_mutex_lock(lock);
+
+ oldValue = *addr;
+ *addr = value;
+
+ pthread_mutex_unlock(lock);
+ return oldValue;
+}
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr) {
+ int result;
+ pthread_mutex_t* lock = SWAP_LOCK(addr);
+
+ pthread_mutex_lock(lock);
+
+ if (*addr == oldvalue) {
+ *addr = newvalue;
+ result = 0;
+ } else {
+ result = 1;
+ }
+ pthread_mutex_unlock(lock);
+ return result;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+ int64_t result;
+ pthread_mutex_t* lock = SWAP_LOCK(addr);
+
+ pthread_mutex_lock(lock);
+ result = *addr;
+ pthread_mutex_unlock(lock);
+ return result;
+}
+
+#else
+
+#error "Unsupported atomic operations for this platform"
+
+#endif
+
+
+
+#if NEED_QUASIATOMICS
+
+/* Note that a spinlock is *not* a good idea in general
+ * since they can introduce subtle issues. For example,
+ * a real-time thread trying to acquire a spinlock already
+ * acquired by another thread will never yeld, making the
+ * CPU loop endlessly!
+ *
+ * However, this code is only used on the Linux simulator
+ * so it's probably ok for us.
+ *
+ * The alternative is to use a pthread mutex, but
+ * these must be initialized before being used, and
+ * then you have the problem of lazily initializing
+ * a mutex without any other synchronization primitive.
+ */
+
+/* global spinlock for all 64-bit quasiatomic operations */
+static int32_t quasiatomic_spinlock = 0;
+
+int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
+ volatile int64_t* addr) {
+ int result;
+
+ while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+ Sleep(0);
+#else
+ sched_yield();
+#endif
+ }
+
+ if (*addr == oldvalue) {
+ *addr = newvalue;
+ result = 0;
+ } else {
+ result = 1;
+ }
+
+ android_atomic_swap(0, &quasiatomic_spinlock);
+
+ return result;
+}
+
+int64_t android_quasiatomic_read_64(volatile int64_t* addr) {
+ int64_t result;
+
+ while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+ Sleep(0);
+#else
+ sched_yield();
+#endif
+ }
+
+ result = *addr;
+ android_atomic_swap(0, &quasiatomic_spinlock);
+
+ return result;
+}
+
+int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr) {
+ int64_t result;
+
+ while (android_atomic_cmpxchg(0, 1, &quasiatomic_spinlock)) {
+#ifdef HAVE_WIN32_THREADS
+ Sleep(0);
+#else
+ sched_yield();
+#endif
+ }
+
+ result = *addr;
+ *addr = value;
+ android_atomic_swap(0, &quasiatomic_spinlock);
+
+ return result;
+}
+
+#endif
diff --git a/libcutils/buffer.c b/libcutils/buffer.c
new file mode 100644
index 0000000..f34b8f8
--- /dev/null
+++ b/libcutils/buffer.c
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "buffer"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "buffer.h"
+#include "loghack.h"
+
+Buffer* bufferCreate(size_t capacity) {
+ Buffer* buffer = malloc(sizeof(Buffer));
+ if (buffer == NULL) {
+ return NULL;
+ }
+ buffer->capacity = capacity;
+ buffer->expected = 0;
+ buffer->data = malloc(capacity);
+ if (buffer->data == NULL) {
+ free(buffer);
+ return NULL;
+ }
+ return buffer;
+}
+
+void bufferFree(Buffer* buffer) {
+ free(buffer->data);
+ free(buffer);
+}
+
+Buffer* bufferWrap(char* data, size_t capacity, size_t size) {
+ Buffer* buffer = malloc(sizeof(Buffer));
+ if (buffer == NULL) {
+ return NULL;
+ }
+
+ buffer->data = data;
+ buffer->capacity = capacity;
+ buffer->size = size;
+ buffer->expected = 0;
+ return buffer;
+}
+
+int bufferPrepareForRead(Buffer* buffer, size_t expected) {
+ if (expected > buffer->capacity) {
+ // Expand buffer.
+ char* expanded = realloc(buffer->data, expected);
+ if (expanded == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ buffer->capacity = expected;
+ buffer->data = expanded;
+ }
+
+ buffer->size = 0;
+ buffer->expected = expected;
+ return 0;
+}
+
+ssize_t bufferRead(Buffer* buffer, int fd) {
+ assert(buffer->size < buffer->expected);
+
+ ssize_t bytesRead = read(fd,
+ buffer->data + buffer->size,
+ buffer->expected - buffer->size);
+
+ if (bytesRead > 0) {
+ buffer->size += bytesRead;
+ return buffer->size;
+ }
+
+ return bytesRead;
+}
+
+void bufferPrepareForWrite(Buffer* buffer) {
+ buffer->remaining = buffer->size;
+}
+
+ssize_t bufferWrite(Buffer* buffer, int fd) {
+ assert(buffer->remaining > 0);
+ assert(buffer->remaining <= buffer->size);
+
+ ssize_t bytesWritten = write(fd,
+ buffer->data + buffer->size - buffer->remaining,
+ buffer->remaining);
+
+ if (bytesWritten >= 0) {
+ buffer->remaining -= bytesWritten;
+
+ LOGD("Buffer bytes written: %d", (int) bytesWritten);
+ LOGD("Buffer size: %d", (int) buffer->size);
+ LOGD("Buffer remaining: %d", (int) buffer->remaining);
+
+ return buffer->remaining;
+ }
+
+ return bytesWritten;
+}
+
diff --git a/libcutils/buffer.h b/libcutils/buffer.h
new file mode 100644
index 0000000..d8bc108
--- /dev/null
+++ b/libcutils/buffer.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+/**
+ * Byte buffer utilities.
+ */
+
+#ifndef __BUFFER_H
+#define __BUFFER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+/**
+ * Byte buffer of known size. Keeps track of how much data has been read
+ * into or written out of the buffer.
+ */
+typedef struct {
+ /** Buffered data. */
+ char* data;
+
+ union {
+ /** For reading. # of bytes we expect. */
+ size_t expected;
+
+ /** For writing. # of bytes to write. */
+ size_t remaining;
+ };
+
+ /** Actual # of bytes in the buffer. */
+ size_t size;
+
+ /** Amount of memory allocated for this buffer. */
+ size_t capacity;
+} Buffer;
+
+/**
+ * Returns true if all data has been read into the buffer.
+ */
+#define bufferReadComplete(buffer) (buffer->expected == buffer->size)
+
+/**
+ * Returns true if the buffer has been completely written.
+ */
+#define bufferWriteComplete(buffer) (buffer->remaining == 0)
+
+/**
+ * Creates a new buffer with the given initial capacity.
+ */
+Buffer* bufferCreate(size_t initialCapacity);
+
+/**
+ * Wraps an existing byte array.
+ */
+Buffer* bufferWrap(char* data, size_t capacity, size_t size);
+
+/**
+ * Frees and its data.
+ */
+void bufferFree(Buffer* buffer);
+
+/**
+ * Prepares buffer to read 'expected' number of bytes. Expands capacity if
+ * necessary. Returns 0 if successful or -1 if an error occurs allocating
+ * memory.
+ */
+int bufferPrepareForRead(Buffer* buffer, size_t expected);
+
+/**
+ * Reads some data into a buffer. Returns -1 in case of an error and sets
+ * errno (see read()). Returns 0 for EOF. Updates buffer->size and returns
+ * the new size after a succesful read.
+ *
+ * Precondition: buffer->size < buffer->expected
+ */
+ssize_t bufferRead(Buffer* buffer, int fd);
+
+/**
+ * Prepares a buffer to be written out.
+ */
+void bufferPrepareForWrite(Buffer* buffer);
+
+/**
+ * Writes data from buffer to the given fd. Returns -1 and sets errno in case
+ * of an error. Updates buffer->remaining and returns the number of remaining
+ * bytes to be written after a successful write.
+ *
+ * Precondition: buffer->remaining > 0
+ */
+ssize_t bufferWrite(Buffer* buffer, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BUFFER_H */
diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c
new file mode 100644
index 0000000..75fa6c6
--- /dev/null
+++ b/libcutils/config_utils.c
@@ -0,0 +1,317 @@
+/*
+ * 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 <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/misc.h>
+
+cnode* config_node(const char *name, const char *value)
+{
+ cnode *node;
+
+ node = calloc(sizeof(cnode), 1);
+ if(node) {
+ node->name = name ? name : "";
+ node->value = value ? value : "";
+ }
+
+ return node;
+}
+
+cnode* config_find(cnode *root, const char *name)
+{
+ cnode *node, *match = NULL;
+
+ /* we walk the whole list, as we need to return the last (newest) entry */
+ for(node = root->first_child; node; node = node->next)
+ if(!strcmp(node->name, name))
+ match = node;
+
+ return match;
+}
+
+static cnode* _config_create(cnode *root, const char *name)
+{
+ cnode *node;
+
+ node = config_node(name, NULL);
+
+ if(root->last_child)
+ root->last_child->next = node;
+ else
+ root->first_child = node;
+
+ root->last_child = node;
+
+ return node;
+}
+
+int config_bool(cnode *root, const char *name, int _default)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(!node)
+ return _default;
+
+ switch(node->value[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+const char* config_str(cnode *root, const char *name, const char *_default)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(!node)
+ return _default;
+ return node->value;
+}
+
+void config_set(cnode *root, const char *name, const char *value)
+{
+ cnode *node;
+
+ node = config_find(root, name);
+ if(node)
+ node->value = value;
+ else {
+ node = _config_create(root, name);
+ node->value = value;
+ }
+}
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_DOT 2
+#define T_OBRACE 3
+#define T_CBRACE 4
+
+typedef struct
+{
+ char *data;
+ char *text;
+ int len;
+ char next;
+} cstate;
+
+static int _lex(cstate *cs, int value)
+{
+ char c;
+ char *s;
+ char *data;
+
+ data = cs->data;
+
+ if(cs->next != 0) {
+ c = cs->next;
+ cs->next = 0;
+ goto got_c;
+ }
+
+restart:
+ for(;;) {
+ c = *data++;
+ got_c:
+ if(isspace(c))
+ continue;
+
+ switch(c) {
+ case 0:
+ return T_EOF;
+
+ case '#':
+ for(;;) {
+ switch(*data) {
+ case 0:
+ cs->data = data;
+ return T_EOF;
+ case '\n':
+ cs->data = data + 1;
+ goto restart;
+ default:
+ data++;
+ }
+ }
+ break;
+
+ case '.':
+ cs->data = data;
+ return T_DOT;
+
+ case '{':
+ cs->data = data;
+ return T_OBRACE;
+
+ case '}':
+ cs->data = data;
+ return T_CBRACE;
+
+ default:
+ s = data - 1;
+
+ if(value) {
+ for(;;) {
+ if(*data == 0) {
+ cs->data = data;
+ break;
+ }
+ if(*data == '\n') {
+ cs->data = data + 1;
+ *data-- = 0;
+ break;
+ }
+ data++;
+ }
+
+ /* strip trailing whitespace */
+ while(data > s){
+ if(!isspace(*data)) break;
+ *data-- = 0;
+ }
+
+ goto got_text;
+ } else {
+ for(;;) {
+ if(isspace(*data)) {
+ *data = 0;
+ cs->data = data + 1;
+ goto got_text;
+ }
+ switch(*data) {
+ case 0:
+ cs->data = data;
+ goto got_text;
+ case '.':
+ case '{':
+ case '}':
+ cs->next = *data;
+ *data = 0;
+ cs->data = data + 1;
+ goto got_text;
+ default:
+ data++;
+ }
+ }
+ }
+ }
+ }
+
+got_text:
+ cs->text = s;
+ return T_TEXT;
+}
+
+#if 0
+char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
+
+static int lex(cstate *cs, int value)
+{
+ int tok = _lex(cs, value);
+ printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
+ tok == T_TEXT ? cs->text : "");
+ return tok;
+}
+#else
+#define lex(cs,v) _lex(cs,v)
+#endif
+
+static int parse_expr(cstate *cs, cnode *node);
+
+static int parse_block(cstate *cs, cnode *node)
+{
+ for(;;){
+ switch(lex(cs, 0)){
+ case T_TEXT:
+ if(parse_expr(cs, node)) return -1;
+ continue;
+
+ case T_CBRACE:
+ return 0;
+
+ default:
+ return -1;
+ }
+ }
+}
+
+static int parse_expr(cstate *cs, cnode *root)
+{
+ cnode *node;
+
+ /* last token was T_TEXT */
+ node = config_find(root, cs->text);
+ if(!node || *node->value)
+ node = _config_create(root, cs->text);
+
+ for(;;) {
+ switch(lex(cs, 1)) {
+ case T_DOT:
+ if(lex(cs, 0) != T_TEXT)
+ return -1;
+ node = _config_create(node, cs->text);
+ continue;
+
+ case T_TEXT:
+ node->value = cs->text;
+ return 0;
+
+ case T_OBRACE:
+ return parse_block(cs, node);
+
+ default:
+ return -1;
+ }
+ }
+}
+
+void config_load(cnode *root, char *data)
+{
+ if(data != 0) {
+ cstate cs;
+ cs.data = data;
+ cs.next = 0;
+
+ for(;;) {
+ switch(lex(&cs, 0)) {
+ case T_TEXT:
+ if(parse_expr(&cs, root))
+ return;
+ break;
+ default:
+ return;
+ }
+ }
+ }
+}
+
+void config_load_file(cnode *root, const char *fn)
+{
+ char *data;
+ data = load_file(fn, 0);
+ config_load(root, data);
+}
diff --git a/libcutils/cpu_info.c b/libcutils/cpu_info.c
new file mode 100644
index 0000000..23dda8a
--- /dev/null
+++ b/libcutils/cpu_info.c
@@ -0,0 +1,83 @@
+/* libs/cutils/cpu_info.c
+**
+** Copyright 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/cpu_info.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// we cache the serial number here.
+// this is also used as a fgets() line buffer when we are reading /proc/cpuinfo
+static char serial_number[100] = { 0 };
+
+extern const char* get_cpu_serial_number(void)
+{
+ if (serial_number[0] == 0)
+ {
+ FILE* file;
+ char* chp, *end;
+ char* whitespace;
+ int length;
+
+ // read serial number from /proc/cpuinfo
+ file = fopen("proc/cpuinfo", "r");
+ if (! file)
+ return NULL;
+
+ while ((chp = fgets(serial_number, sizeof(serial_number), file)) != NULL)
+ {
+ // look for something like "Serial : 999206122a03591c"
+
+ if (strncmp(chp, "Serial", 6) != 0)
+ continue;
+
+ chp = strchr(chp, ':');
+ if (!chp)
+ continue;
+
+ // skip colon and whitespace
+ while ( *(++chp) == ' ') {}
+
+ // truncate trailing whitespace
+ end = chp;
+ while (*end && *end != ' ' && *end != '\t' && *end != '\n' && *end != '\r')
+ ++end;
+ *end = 0;
+
+ whitespace = strchr(chp, ' ');
+ if (whitespace)
+ *whitespace = 0;
+ whitespace = strchr(chp, '\t');
+ if (whitespace)
+ *whitespace = 0;
+ whitespace = strchr(chp, '\r');
+ if (whitespace)
+ *whitespace = 0;
+ whitespace = strchr(chp, '\n');
+ if (whitespace)
+ *whitespace = 0;
+
+ // shift serial number to beginning of the buffer
+ memmove(serial_number, chp, strlen(chp) + 1);
+ break;
+ }
+
+ fclose(file);
+ }
+
+ return (serial_number[0] ? serial_number : NULL);
+}
diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c
new file mode 100644
index 0000000..be14af6
--- /dev/null
+++ b/libcutils/dir_hash.c
@@ -0,0 +1,334 @@
+/*
+ * 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 <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sha1.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <resolv.h>
+
+#include <cutils/dir_hash.h>
+
+/**
+ * Copies, if it fits within max_output_string bytes, into output_string
+ * a hash of the contents, size, permissions, uid, and gid of the file
+ * specified by path, using the specified algorithm. Returns the length
+ * of the output string, or a negative number if the buffer is too short.
+ */
+int get_file_hash(HashAlgorithm algorithm, const char *path,
+ char *output_string, size_t max_output_string) {
+ SHA1_CTX context;
+ struct stat sb;
+ unsigned char md[SHA1_DIGEST_LENGTH];
+ int used;
+ size_t n;
+
+ if (algorithm != SHA_1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (stat(path, &sb) != 0) {
+ return -1;
+ }
+
+ if (S_ISLNK(sb.st_mode)) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = readlink(path, buf, sizeof(buf));
+ if (len < 0) {
+ return -1;
+ }
+
+ SHA1Init(&context);
+ SHA1Update(&context, (unsigned char *) buf, len);
+ SHA1Final(md, &context);
+ } else if (S_ISREG(sb.st_mode)) {
+ char buf[10000];
+ FILE *f = fopen(path, "rb");
+ int len;
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ SHA1Init(&context);
+
+ while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
+ SHA1Update(&context, (unsigned char *) buf, len);
+ }
+
+ if (ferror(f)) {
+ fclose(f);
+ return -1;
+ }
+
+ fclose(f);
+ SHA1Final(md, &context);
+ }
+
+ if (S_ISLNK(sb.st_mode) || S_ISREG(sb.st_mode)) {
+ used = b64_ntop(md, SHA1_DIGEST_LENGTH,
+ output_string, max_output_string);
+ if (used < 0) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ n = snprintf(output_string + used, max_output_string - used,
+ " %d 0%o %d %d", (int) sb.st_size, sb.st_mode,
+ (int) sb.st_uid, (int) sb.st_gid);
+ } else {
+ n = snprintf(output_string, max_output_string,
+ "- - 0%o %d %d", sb.st_mode,
+ (int) sb.st_uid, (int) sb.st_gid);
+ }
+
+ if (n >= max_output_string - used) {
+ errno = ENOSPC;
+ return -(used + n);
+ }
+
+ return used + n;
+}
+
+struct list {
+ char *name;
+ struct list *next;
+};
+
+static int cmp(const void *a, const void *b) {
+ struct list *const *ra = a;
+ struct list *const *rb = b;
+
+ return strcmp((*ra)->name, (*rb)->name);
+}
+
+static int recurse(HashAlgorithm algorithm, const char *directory_path,
+ struct list **out) {
+ struct list *list = NULL;
+ struct list *f;
+
+ struct dirent *de;
+ DIR *d = opendir(directory_path);
+
+ if (d == NULL) {
+ return -1;
+ }
+
+ while ((de = readdir(d)) != NULL) {
+ if (strcmp(de->d_name, ".") == 0) {
+ continue;
+ }
+ if (strcmp(de->d_name, "..") == 0) {
+ continue;
+ }
+
+ char *name = malloc(strlen(de->d_name) + 1);
+ struct list *node = malloc(sizeof(struct list));
+
+ if (name == NULL || node == NULL) {
+ struct list *next;
+ for (f = list; f != NULL; f = next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+
+ free(name);
+ free(node);
+ return -1;
+ }
+
+ strcpy(name, de->d_name);
+
+ node->name = name;
+ node->next = list;
+ list = node;
+ }
+
+ closedir(d);
+
+ for (f = list; f != NULL; f = f->next) {
+ struct stat sb;
+ char *name;
+ char outstr[NAME_MAX + 100];
+ char *keep;
+ struct list *res;
+
+ name = malloc(strlen(f->name) + strlen(directory_path) + 2);
+ if (name == NULL) {
+ struct list *next;
+ for (f = list; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ for (f = *out; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ *out = NULL;
+ return -1;
+ }
+
+ sprintf(name, "%s/%s", directory_path, f->name);
+
+ int len = get_file_hash(algorithm, name,
+ outstr, sizeof(outstr));
+ if (len < 0) {
+ // should not happen
+ return -1;
+ }
+
+ keep = malloc(len + strlen(name) + 3);
+ res = malloc(sizeof(struct list));
+
+ if (keep == NULL || res == NULL) {
+ struct list *next;
+ for (f = list; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ for (f = *out; f != NULL; f = f->next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+ *out = NULL;
+
+ free(keep);
+ free(res);
+ return -1;
+ }
+
+ sprintf(keep, "%s %s\n", name, outstr);
+
+ res->name = keep;
+ res->next = *out;
+ *out = res;
+
+ if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) {
+ if (recurse(algorithm, name, out) < 0) {
+ struct list *next;
+ for (f = list; f != NULL; f = next) {
+ next = f->next;
+ free(f->name);
+ free(f);
+ }
+
+ return -1;
+ }
+ }
+ }
+
+ struct list *next;
+ for (f = list; f != NULL; f = next) {
+ next = f->next;
+
+ free(f->name);
+ free(f);
+ }
+}
+
+/**
+ * Allocates a string containing the names and hashes of all files recursively
+ * reached under the specified directory_path, using the specified algorithm.
+ * The string is returned as *output_string; the return value is the length
+ * of the string, or a negative number if there was a failure.
+ */
+int get_recursive_hash_manifest(HashAlgorithm algorithm,
+ const char *directory_path,
+ char **output_string) {
+ struct list *out = NULL;
+ struct list *r;
+ struct list **list;
+ int count = 0;
+ int len = 0;
+ int retlen = 0;
+ int i;
+ char *buf;
+
+ if (recurse(algorithm, directory_path, &out) < 0) {
+ return -1;
+ }
+
+ for (r = out; r != NULL; r = r->next) {
+ count++;
+ len += strlen(r->name);
+ }
+
+ list = malloc(count * sizeof(struct list *));
+ if (list == NULL) {
+ struct list *next;
+ for (r = out; r != NULL; r = next) {
+ next = r->next;
+ free(r->name);
+ free(r);
+ }
+ return -1;
+ }
+
+ count = 0;
+ for (r = out; r != NULL; r = r->next) {
+ list[count++] = r;
+ }
+
+ qsort(list, count, sizeof(struct list *), cmp);
+
+ buf = malloc(len + 1);
+ if (buf == NULL) {
+ struct list *next;
+ for (r = out; r != NULL; r = next) {
+ next = r->next;
+ free(r->name);
+ free(r);
+ }
+ free(list);
+ return -1;
+ }
+
+ for (i = 0; i < count; i++) {
+ int n = strlen(list[i]->name);
+
+ strcpy(buf + retlen, list[i]->name);
+ retlen += n;
+ }
+
+ free(list);
+
+ struct list *next;
+ for (r = out; r != NULL; r = next) {
+ next = r->next;
+
+ free(r->name);
+ free(r);
+ }
+
+ *output_string = buf;
+ return retlen;
+}
diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c
new file mode 100644
index 0000000..1ced147
--- /dev/null
+++ b/libcutils/dlmalloc_stubs.c
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/* No-op stubs for functions defined in system/bionic/bionic/dlmalloc.c.
+ */
+void dlmalloc_walk_free_pages()
+{
+}
+
+void dlmalloc_walk_heap()
+{
+}
+
+void dlmalloc_trim()
+{
+}
diff --git a/libcutils/fdevent.c b/libcutils/fdevent.c
new file mode 100644
index 0000000..4cf46fa
--- /dev/null
+++ b/libcutils/fdevent.c
@@ -0,0 +1,506 @@
+/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
+**
+** Copyright 2006, Brian Swetland <swetland@frotz.net>
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <fcntl.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include <cutils/fdevent.h>
+
+#define TRACE(x...) fprintf(stderr,x)
+
+#define DEBUG 0
+
+static void fatal(const char *fn, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:", fn);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ abort();
+}
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+static void dump_fde(fdevent *fde, const char *info)
+{
+ fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+ fde->state & FDE_READ ? 'R' : ' ',
+ fde->state & FDE_WRITE ? 'W' : ' ',
+ fde->state & FDE_ERROR ? 'E' : ' ',
+ info);
+}
+#else
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+#define FDE_CREATED 0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+
+static fdevent list_pending = {
+ .next = &list_pending,
+ .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+#ifdef CRAPTASTIC
+//HAVE_EPOLL
+
+#include <sys/epoll.h>
+
+static int epoll_fd = -1;
+
+static void fdevent_init()
+{
+ /* XXX: what's a good size for the passed in hint? */
+ epoll_fd = epoll_create(256);
+
+ if(epoll_fd < 0) {
+ perror("epoll_create() failed");
+ exit(1);
+ }
+
+ /* mark for close-on-exec */
+ fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ struct epoll_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+#if 0
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+#endif
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ struct epoll_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+ /* technically we only need to delete if we
+ ** were actively monitoring events, but let's
+ ** be aggressive and do it anyway, just in case
+ ** something's out of sync
+ */
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ struct epoll_event ev;
+ int active;
+
+ active = (fde->state & FDE_EVENTMASK) != 0;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+ if(events & FDE_READ) ev.events |= EPOLLIN;
+ if(events & FDE_WRITE) ev.events |= EPOLLOUT;
+ if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(active) {
+ /* we're already active. if we're changing to *no*
+ ** events being monitored, we need to delete, otherwise
+ ** we need to just modify
+ */
+ if(ev.events) {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ } else {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ }
+ } else {
+ /* we're not active. if we're watching events, we need
+ ** to add, otherwise we can just do nothing
+ */
+ if(ev.events) {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ }
+ }
+}
+
+static void fdevent_process()
+{
+ struct epoll_event events[256];
+ fdevent *fde;
+ int i, n;
+
+ n = epoll_wait(epoll_fd, events, 256, -1);
+
+ if(n < 0) {
+ if(errno == EINTR) return;
+ perror("epoll_wait");
+ exit(1);
+ }
+
+ for(i = 0; i < n; i++) {
+ struct epoll_event *ev = events + i;
+ fde = ev->data.ptr;
+
+ if(ev->events & EPOLLIN) {
+ fde->events |= FDE_READ;
+ }
+ if(ev->events & EPOLLOUT) {
+ fde->events |= FDE_WRITE;
+ }
+ if(ev->events & (EPOLLERR | EPOLLHUP)) {
+ fde->events |= FDE_ERROR;
+ }
+ if(fde->events) {
+ if(fde->state & FDE_PENDING) continue;
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue(fde);
+ }
+ }
+}
+
+#else /* USE_SELECT */
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+
+static fd_set read_fds;
+static fd_set write_fds;
+static fd_set error_fds;
+
+static int select_n = 0;
+
+static void fdevent_init(void)
+{
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ FD_ZERO(&error_fds);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ if(fde->fd >= select_n) {
+ select_n = fde->fd + 1;
+ }
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ int i, n;
+
+ FD_CLR(fde->fd, &read_fds);
+ FD_CLR(fde->fd, &write_fds);
+ FD_CLR(fde->fd, &error_fds);
+
+ for(n = 0, i = 0; i < select_n; i++) {
+ if(fd_table[i] != 0) n = i;
+ }
+ select_n = n + 1;
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ if(events & FDE_READ) {
+ FD_SET(fde->fd, &read_fds);
+ } else {
+ FD_CLR(fde->fd, &read_fds);
+ }
+ if(events & FDE_WRITE) {
+ FD_SET(fde->fd, &write_fds);
+ } else {
+ FD_CLR(fde->fd, &write_fds);
+ }
+ if(events & FDE_ERROR) {
+ FD_SET(fde->fd, &error_fds);
+ } else {
+ FD_CLR(fde->fd, &error_fds);
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+}
+
+static void fdevent_process()
+{
+ int i, n;
+ fdevent *fde;
+ unsigned events;
+ fd_set rfd, wfd, efd;
+
+ memcpy(&rfd, &read_fds, sizeof(fd_set));
+ memcpy(&wfd, &write_fds, sizeof(fd_set));
+ memcpy(&efd, &error_fds, sizeof(fd_set));
+
+ n = select(select_n, &rfd, &wfd, &efd, 0);
+
+ if(n < 0) {
+ if(errno == EINTR) return;
+ perror("select");
+ return;
+ }
+
+ for(i = 0; (i < select_n) && (n > 0); i++) {
+ events = 0;
+ if(FD_ISSET(i, &rfd)) events |= FDE_READ;
+ if(FD_ISSET(i, &wfd)) events |= FDE_WRITE;
+ if(FD_ISSET(i, &efd)) events |= FDE_ERROR;
+
+ if(events) {
+ n--;
+
+ fde = fd_table[i];
+ if(fde == 0) FATAL("missing fde for fd %d\n", i);
+
+ fde->events |= events;
+
+ if(fde->state & FDE_PENDING) continue;
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue(fde);
+ }
+ }
+}
+
+#endif
+
+static void fdevent_register(fdevent *fde)
+{
+ if(fde->fd < 0) {
+ FATAL("bogus negative fd (%d)\n", fde->fd);
+ }
+
+ if(fde->fd >= fd_table_max) {
+ int oldmax = fd_table_max;
+ if(fde->fd > 32000) {
+ FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+ }
+ if(fd_table_max == 0) {
+ fdevent_init();
+ fd_table_max = 256;
+ }
+ while(fd_table_max <= fde->fd) {
+ fd_table_max *= 2;
+ }
+ fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
+ if(fd_table == 0) {
+ FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+ }
+ memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+ }
+
+ fd_table[fde->fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+ if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
+ FATAL("fd out of range (%d)\n", fde->fd);
+ }
+
+ if(fd_table[fde->fd] != fde) {
+ FATAL("fd_table out of sync");
+ }
+
+ fd_table[fde->fd] = 0;
+
+ if(!(fde->state & FDE_DONT_CLOSE)) {
+ dump_fde(fde, "close");
+ close(fde->fd);
+ }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+ fdevent *list = &list_pending;
+
+ node->next = list;
+ node->prev = list->prev;
+ node->prev->next = node;
+ list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+ fdevent *list = &list_pending;
+ fdevent *node = list->next;
+
+ if(node == list) return 0;
+
+ list->next = node->next;
+ list->next->prev = list;
+ node->next = 0;
+ node->prev = 0;
+
+ return node;
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+ fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+ if(fde == 0) return 0;
+ fdevent_install(fde, fd, func, arg);
+ fde->state |= FDE_CREATED;
+ return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+ if(fde == 0) return;
+ if(!(fde->state & FDE_CREATED)) {
+ FATAL("fde %p not created by fdevent_create()\n", fde);
+ }
+ fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
+{
+ memset(fde, 0, sizeof(fdevent));
+ fde->state = FDE_ACTIVE;
+ fde->fd = fd;
+ fde->func = func;
+ fde->arg = arg;
+
+#ifndef HAVE_WINSOCK
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+ fdevent_register(fde);
+ dump_fde(fde, "connect");
+ fdevent_connect(fde);
+ fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+ if(fde->state & FDE_PENDING) {
+ fdevent_plist_remove(fde);
+ }
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_disconnect(fde);
+ dump_fde(fde, "disconnect");
+ fdevent_unregister(fde);
+ }
+
+ fde->state = 0;
+ fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+ events &= FDE_EVENTMASK;
+
+ if((fde->state & FDE_EVENTMASK) == events) return;
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_update(fde, events);
+ dump_fde(fde, "update");
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(fde->state & FDE_PENDING) {
+ /* if we're pending, make sure
+ ** we don't signal an event that
+ ** is no longer wanted.
+ */
+ fde->events &= (~events);
+ if(fde->events == 0) {
+ fdevent_plist_remove(fde);
+ fde->state &= (~FDE_PENDING);
+ }
+ }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_loop()
+{
+ fdevent *fde;
+
+ for(;;) {
+#if DEBUG
+ fprintf(stderr,"--- ---- waiting for events\n");
+#endif
+ fdevent_process();
+
+ while((fde = fdevent_plist_dequeue())) {
+ unsigned events = fde->events;
+ fde->events = 0;
+ fde->state &= (~FDE_PENDING);
+ dump_fde(fde, "callback");
+ fde->func(fde->fd, events, fde->arg);
+ }
+ }
+}
+
diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c
new file mode 100644
index 0000000..e29bc24
--- /dev/null
+++ b/libcutils/hashmap.c
@@ -0,0 +1,350 @@
+/*
+ * 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/hashmap.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/threads.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct Entry Entry;
+struct Entry {
+ void* key;
+ int hash;
+ void* value;
+ Entry* next;
+};
+
+struct Hashmap {
+ Entry** buckets;
+ size_t bucketCount;
+ int (*hash)(void* key);
+ bool (*equals)(void* keyA, void* keyB);
+ mutex_t lock;
+ size_t size;
+};
+
+Hashmap* hashmapCreate(size_t initialCapacity,
+ int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
+ assert(hash != NULL);
+ assert(equals != NULL);
+
+ Hashmap* map = malloc(sizeof(Hashmap));
+ if (map == NULL) {
+ return NULL;
+ }
+
+ // 0.75 load factor.
+ size_t minimumBucketCount = initialCapacity * 4 / 3;
+ map->bucketCount = 1;
+ while (map->bucketCount <= minimumBucketCount) {
+ // Bucket count must be power of 2.
+ map->bucketCount <<= 1;
+ }
+
+ map->buckets = calloc(map->bucketCount, sizeof(Entry*));
+ if (map->buckets == NULL) {
+ free(map);
+ return NULL;
+ }
+
+ map->size = 0;
+
+ map->hash = hash;
+ map->equals = equals;
+
+ mutex_init(&map->lock);
+
+ return map;
+}
+
+/**
+ * Hashes the given key.
+ */
+static inline int hashKey(Hashmap* map, void* key) {
+ int h = map->hash(key);
+
+ // We apply this secondary hashing discovered by Doug Lea to defend
+ // against bad hashes.
+ h += ~(h << 9);
+ h ^= (((unsigned int) h) >> 14);
+ h += (h << 4);
+ h ^= (((unsigned int) h) >> 10);
+
+ return h;
+}
+
+size_t hashmapSize(Hashmap* map) {
+ return map->size;
+}
+
+static inline size_t calculateIndex(size_t bucketCount, int hash) {
+ return ((size_t) hash) & (bucketCount - 1);
+}
+
+static void expandIfNecessary(Hashmap* map) {
+ // If the load factor exceeds 0.75...
+ if (map->size > (map->bucketCount * 3 / 4)) {
+ // Start off with a 0.33 load factor.
+ size_t newBucketCount = map->bucketCount << 1;
+ Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*));
+ if (newBuckets == NULL) {
+ // Abort expansion.
+ return;
+ }
+
+ // Move over existing entries.
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ Entry* next = entry->next;
+ size_t index = calculateIndex(newBucketCount, entry->hash);
+ entry->next = newBuckets[index];
+ newBuckets[index] = entry;
+ entry = next;
+ }
+ }
+
+ // Copy over internals.
+ free(map->buckets);
+ map->buckets = newBuckets;
+ map->bucketCount = newBucketCount;
+ }
+}
+
+void hashmapLock(Hashmap* map) {
+ mutex_lock(&map->lock);
+}
+
+void hashmapUnlock(Hashmap* map) {
+ mutex_unlock(&map->lock);
+}
+
+void hashmapFree(Hashmap* map) {
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ Entry* next = entry->next;
+ free(entry);
+ entry = next;
+ }
+ }
+ free(map->buckets);
+ mutex_destroy(&map->lock);
+ free(map);
+}
+
+int hashmapHash(void* key, size_t keySize) {
+ int h = keySize;
+ char* data = (char*) key;
+ size_t i;
+ for (i = 0; i < keySize; i++) {
+ h = h * 31 + *data;
+ data++;
+ }
+ return h;
+}
+
+static Entry* createEntry(void* key, int hash, void* value) {
+ Entry* entry = malloc(sizeof(Entry));
+ if (entry == NULL) {
+ return NULL;
+ }
+ entry->key = key;
+ entry->hash = hash;
+ entry->value = value;
+ entry->next = NULL;
+ return entry;
+}
+
+static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,
+ bool (*equals)(void*, void*)) {
+ if (keyA == keyB) {
+ return true;
+ }
+ if (hashA != hashB) {
+ return false;
+ }
+ return equals(keyA, keyB);
+}
+
+void* hashmapPut(Hashmap* map, void* key, void* value) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry** p = &(map->buckets[index]);
+ while (true) {
+ Entry* current = *p;
+
+ // Add a new entry.
+ if (current == NULL) {
+ *p = createEntry(key, hash, value);
+ if (*p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ map->size++;
+ expandIfNecessary(map);
+ return NULL;
+ }
+
+ // Replace existing entry.
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ void* oldValue = current->value;
+ current->value = value;
+ return oldValue;
+ }
+
+ // Move to next entry.
+ p = &current->next;
+ }
+}
+
+void* hashmapGet(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry* entry = map->buckets[index];
+ while (entry != NULL) {
+ if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+ return entry->value;
+ }
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+bool hashmapContainsKey(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry* entry = map->buckets[index];
+ while (entry != NULL) {
+ if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
+ return true;
+ }
+ entry = entry->next;
+ }
+
+ return false;
+}
+
+void* hashmapMemoize(Hashmap* map, void* key,
+ void* (*initialValue)(void* key, void* context), void* context) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ Entry** p = &(map->buckets[index]);
+ while (true) {
+ Entry* current = *p;
+
+ // Add a new entry.
+ if (current == NULL) {
+ *p = createEntry(key, hash, NULL);
+ if (*p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ void* value = initialValue(key, context);
+ (*p)->value = value;
+ map->size++;
+ expandIfNecessary(map);
+ return value;
+ }
+
+ // Return existing value.
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ return current->value;
+ }
+
+ // Move to next entry.
+ p = &current->next;
+ }
+}
+
+void* hashmapRemove(Hashmap* map, void* key) {
+ int hash = hashKey(map, key);
+ size_t index = calculateIndex(map->bucketCount, hash);
+
+ // Pointer to the current entry.
+ Entry** p = &(map->buckets[index]);
+ Entry* current;
+ while ((current = *p) != NULL) {
+ if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
+ void* value = current->value;
+ *p = current->next;
+ free(current);
+ map->size--;
+ return value;
+ }
+
+ p = &current->next;
+ }
+
+ return NULL;
+}
+
+void hashmapForEach(Hashmap* map,
+ bool (*callback)(void* key, void* value, void* context),
+ void* context) {
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ if (!callback(entry->key, entry->value, context)) {
+ return;
+ }
+ entry = entry->next;
+ }
+ }
+}
+
+size_t hashmapCurrentCapacity(Hashmap* map) {
+ size_t bucketCount = map->bucketCount;
+ return bucketCount * 3 / 4;
+}
+
+size_t hashmapCountCollisions(Hashmap* map) {
+ size_t collisions = 0;
+ size_t i;
+ for (i = 0; i < map->bucketCount; i++) {
+ Entry* entry = map->buckets[i];
+ while (entry != NULL) {
+ if (entry->next != NULL) {
+ collisions++;
+ }
+ entry = entry->next;
+ }
+ }
+ return collisions;
+}
+
+int hashmapIntHash(void* key) {
+ // Return the key value itself.
+ return *((int*) key);
+}
+
+bool hashmapIntEquals(void* keyA, void* keyB) {
+ int a = *((int*) keyA);
+ int b = *((int*) keyB);
+ return a == b;
+}
diff --git a/libcutils/load_file.c b/libcutils/load_file.c
new file mode 100644
index 0000000..99f2965
--- /dev/null
+++ b/libcutils/load_file.c
@@ -0,0 +1,51 @@
+/* libs/cutils/load_file.c
+**
+** 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 <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+void *load_file(const char *fn, unsigned *_sz)
+{
+ char *data;
+ int sz;
+ int fd;
+
+ data = 0;
+ fd = open(fn, O_RDONLY);
+ if(fd < 0) return 0;
+
+ sz = lseek(fd, 0, SEEK_END);
+ if(sz < 0) goto oops;
+
+ if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
+
+ data = (char*) malloc(sz + 1);
+ if(data == 0) goto oops;
+
+ if(read(fd, data, sz) != sz) goto oops;
+ close(fd);
+ data[sz] = 0;
+
+ if(_sz) *_sz = sz;
+ return data;
+
+oops:
+ close(fd);
+ if(data != 0) free(data);
+ return 0;
+}
diff --git a/libcutils/loghack.h b/libcutils/loghack.h
new file mode 100644
index 0000000..2bfffe4
--- /dev/null
+++ b/libcutils/loghack.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/**
+ * This is a temporary hack to enable logging from cutils.
+ */
+
+#ifndef _CUTILS_LOGHACK_H
+#define _CUTILS_LOGHACK_H
+
+#ifdef HAVE_ANDROID_OS
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#define LOG(level, ...) \
+ ((void)printf("cutils:" level "/" LOG_TAG ": " __VA_ARGS__))
+#define LOGV(...) LOG("V", __VA_ARGS__)
+#define LOGD(...) LOG("D", __VA_ARGS__)
+#define LOGI(...) LOG("I", __VA_ARGS__)
+#define LOGW(...) LOG("W", __VA_ARGS__)
+#define LOGE(...) LOG("E", __VA_ARGS__)
+#define LOG_ALWAYS_FATAL(...) do { LOGE(__VA_ARGS__); exit(1); } while (0)
+#endif
+
+#endif // _CUTILS_LOGHACK_H
diff --git a/libcutils/memory.c b/libcutils/memory.c
new file mode 100644
index 0000000..ef6c7e6
--- /dev/null
+++ b/libcutils/memory.c
@@ -0,0 +1,87 @@
+/*
+ * 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/memory.h>
+
+void android_memset16(uint16_t* dst, uint16_t value, size_t size)
+{
+ size >>= 1;
+ while (size--) {
+ *dst++ = value;
+ }
+}
+
+void android_memset32(uint32_t* dst, uint32_t value, size_t size)
+{
+ size >>= 2;
+ while (size--) {
+ *dst++ = value;
+ }
+}
+
+#if !HAVE_STRLCPY
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/* Implementation of strlcpy() for platforms that don't already have it. */
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+#endif
diff --git a/libcutils/memset32.S b/libcutils/memset32.S
new file mode 100644
index 0000000..4697265
--- /dev/null
+++ b/libcutils/memset32.S
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+/*
+ * memset32.S
+ *
+ */
+
+ .text
+ .align
+
+ .global android_memset32
+ .type android_memset32, %function
+ .global android_memset16
+ .type android_memset16, %function
+
+ /*
+ * Optimized memset32 and memset16 for ARM.
+ *
+ * void android_memset16(uint16_t* dst, uint16_t value, size_t size);
+ * void android_memset32(uint32_t* dst, uint32_t value, size_t size);
+ *
+ */
+
+android_memset16:
+ .fnstart
+ cmp r2, #1
+ bxle lr
+
+ /* expand the data to 32 bits */
+ mov r1, r1, lsl #16
+ orr r1, r1, r1, lsr #16
+
+ /* align to 32 bits */
+ tst r0, #2
+ strneh r1, [r0], #2
+ subne r2, r2, #2
+ .fnend
+
+android_memset32:
+ .fnstart
+ .save {lr}
+ str lr, [sp, #-4]!
+
+ /* align the destination to a cache-line */
+ mov r12, r1
+ mov lr, r1
+ rsb r3, r0, #0
+ ands r3, r3, #0x1C
+ beq .Laligned32
+ cmp r3, r2
+ andhi r3, r2, #0x1C
+ sub r2, r2, r3
+
+ /* conditionally writes 0 to 7 words (length in r3) */
+ movs r3, r3, lsl #28
+ stmcsia r0!, {r1, lr}
+ stmcsia r0!, {r1, lr}
+ stmmiia r0!, {r1, lr}
+ movs r3, r3, lsl #2
+ strcs r1, [r0], #4
+
+.Laligned32:
+ mov r3, r1
+1: subs r2, r2, #32
+ stmhsia r0!, {r1,r3,r12,lr}
+ stmhsia r0!, {r1,r3,r12,lr}
+ bhs 1b
+ add r2, r2, #32
+
+ /* conditionally stores 0 to 30 bytes */
+ movs r2, r2, lsl #28
+ stmcsia r0!, {r1,r3,r12,lr}
+ stmmiia r0!, {r1,lr}
+ movs r2, r2, lsl #2
+ strcs r1, [r0], #4
+ strmih lr, [r0], #2
+
+ ldr lr, [sp], #4
+ bx lr
+ .fnend
diff --git a/libcutils/mq.c b/libcutils/mq.c
new file mode 100644
index 0000000..3b65f1f
--- /dev/null
+++ b/libcutils/mq.c
@@ -0,0 +1,1357 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "mq"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#include <cutils/array.h>
+#include <cutils/hashmap.h>
+#include <cutils/selector.h>
+
+#include "loghack.h"
+#include "buffer.h"
+
+/** Number of dead peers to remember. */
+#define PEER_HISTORY (16)
+
+typedef struct sockaddr SocketAddress;
+typedef struct sockaddr_un UnixAddress;
+
+/**
+ * Process/user/group ID. We don't use ucred directly because it's only
+ * available on Linux.
+ */
+typedef struct {
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+} Credentials;
+
+/** Listens for bytes coming from remote peers. */
+typedef void BytesListener(Credentials credentials, char* bytes, size_t size);
+
+/** Listens for the deaths of remote peers. */
+typedef void DeathListener(pid_t pid);
+
+/** Types of packets. */
+typedef enum {
+ /** Request for a connection to another peer. */
+ CONNECTION_REQUEST,
+
+ /** A connection to another peer. */
+ CONNECTION,
+
+ /** Reports a failed connection attempt. */
+ CONNECTION_ERROR,
+
+ /** A generic packet of bytes. */
+ BYTES,
+} PacketType;
+
+typedef enum {
+ /** Reading a packet header. */
+ READING_HEADER,
+
+ /** Waiting for a connection from the master. */
+ ACCEPTING_CONNECTION,
+
+ /** Reading bytes. */
+ READING_BYTES,
+} InputState;
+
+/** A packet header. */
+// TODO: Use custom headers for master->peer, peer->master, peer->peer.
+typedef struct {
+ PacketType type;
+ union {
+ /** Packet size. Used for BYTES. */
+ size_t size;
+
+ /** Credentials. Used for CONNECTION and CONNECTION_REQUEST. */
+ Credentials credentials;
+ };
+} Header;
+
+/** A packet which will be sent to a peer. */
+typedef struct OutgoingPacket OutgoingPacket;
+struct OutgoingPacket {
+ /** Packet header. */
+ Header header;
+
+ union {
+ /** Connection to peer. Used with CONNECTION. */
+ int socket;
+
+ /** Buffer of bytes. Used with BYTES. */
+ Buffer* bytes;
+ };
+
+ /** Frees all resources associated with this packet. */
+ void (*free)(OutgoingPacket* packet);
+
+ /** Optional context. */
+ void* context;
+
+ /** Next packet in the queue. */
+ OutgoingPacket* nextPacket;
+};
+
+/** Represents a remote peer. */
+typedef struct PeerProxy PeerProxy;
+
+/** Local peer state. You typically have one peer per process. */
+typedef struct {
+ /** This peer's PID. */
+ pid_t pid;
+
+ /**
+ * Map from pid to peer proxy. The peer has a peer proxy for each remote
+ * peer it's connected to.
+ *
+ * Acquire mutex before use.
+ */
+ Hashmap* peerProxies;
+
+ /** Manages I/O. */
+ Selector* selector;
+
+ /** Used to synchronize operations with the selector thread. */
+ pthread_mutex_t mutex;
+
+ /** Is this peer the master? */
+ bool master;
+
+ /** Peer proxy for the master. */
+ PeerProxy* masterProxy;
+
+ /** Listens for packets from remote peers. */
+ BytesListener* onBytes;
+
+ /** Listens for deaths of remote peers. */
+ DeathListener* onDeath;
+
+ /** Keeps track of recently dead peers. Requires mutex. */
+ pid_t deadPeers[PEER_HISTORY];
+ size_t deadPeerCursor;
+} Peer;
+
+struct PeerProxy {
+ /** Credentials of the remote process. */
+ Credentials credentials;
+
+ /** Keeps track of data coming in from the remote peer. */
+ InputState inputState;
+ Buffer* inputBuffer;
+ PeerProxy* connecting;
+
+ /** File descriptor for this peer. */
+ SelectableFd* fd;
+
+ /**
+ * Queue of packets to be written out to the remote peer.
+ *
+ * Requires mutex.
+ */
+ // TODO: Limit queue length.
+ OutgoingPacket* currentPacket;
+ OutgoingPacket* lastPacket;
+
+ /** Used to write outgoing header. */
+ Buffer outgoingHeader;
+
+ /** True if this is the master's proxy. */
+ bool master;
+
+ /** Reference back to the local peer. */
+ Peer* peer;
+
+ /**
+ * Used in master only. Maps this peer proxy to other peer proxies to
+ * which the peer has been connected to. Maps pid to PeerProxy. Helps
+ * keep track of which connections we've sent to whom.
+ */
+ Hashmap* connections;
+};
+
+/** Server socket path. */
+static const char* MASTER_PATH = "/master.peer";
+
+/** Credentials of the master peer. */
+static const Credentials MASTER_CREDENTIALS = {0, 0, 0};
+
+/** Creates a peer proxy and adds it to the peer proxy map. */
+static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials);
+
+/** Sets the non-blocking flag on a descriptor. */
+static void setNonBlocking(int fd) {
+ int flags;
+ if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
+ LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno));
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno));
+ }
+}
+
+/** Closes a fd and logs a warning if the close fails. */
+static void closeWithWarning(int fd) {
+ int result = close(fd);
+ if (result == -1) {
+ LOGW("close() error: %s", strerror(errno));
+ }
+}
+
+/** Hashes pid_t keys. */
+static int pidHash(void* key) {
+ pid_t* pid = (pid_t*) key;
+ return (int) (*pid);
+}
+
+/** Compares pid_t keys. */
+static bool pidEquals(void* keyA, void* keyB) {
+ pid_t* a = (pid_t*) keyA;
+ pid_t* b = (pid_t*) keyB;
+ return *a == *b;
+}
+
+/** Gets the master address. Not thread safe. */
+static UnixAddress* getMasterAddress() {
+ static UnixAddress masterAddress;
+ static bool initialized = false;
+ if (initialized == false) {
+ masterAddress.sun_family = AF_LOCAL;
+ strcpy(masterAddress.sun_path, MASTER_PATH);
+ initialized = true;
+ }
+ return &masterAddress;
+}
+
+/** Gets exclusive access to the peer for this thread. */
+static void peerLock(Peer* peer) {
+ pthread_mutex_lock(&peer->mutex);
+}
+
+/** Releases exclusive access to the peer. */
+static void peerUnlock(Peer* peer) {
+ pthread_mutex_unlock(&peer->mutex);
+}
+
+/** Frees a simple, i.e. header-only, outgoing packet. */
+static void outgoingPacketFree(OutgoingPacket* packet) {
+ LOGD("Freeing outgoing packet.");
+ free(packet);
+}
+
+/**
+ * Prepare to read a new packet from the peer.
+ */
+static void peerProxyExpectHeader(PeerProxy* peerProxy) {
+ peerProxy->inputState = READING_HEADER;
+ bufferPrepareForRead(peerProxy->inputBuffer, sizeof(Header));
+}
+
+/** Sets up the buffer for the outgoing header. */
+static void peerProxyPrepareOutgoingHeader(PeerProxy* peerProxy) {
+ peerProxy->outgoingHeader.data
+ = (char*) &(peerProxy->currentPacket->header);
+ peerProxy->outgoingHeader.size = sizeof(Header);
+ bufferPrepareForWrite(&peerProxy->outgoingHeader);
+}
+
+/** Adds a packet to the end of the queue. Callers must have the mutex. */
+static void peerProxyEnqueueOutgoingPacket(PeerProxy* peerProxy,
+ OutgoingPacket* newPacket) {
+ newPacket->nextPacket = NULL; // Just in case.
+ if (peerProxy->currentPacket == NULL) {
+ // The queue is empty.
+ peerProxy->currentPacket = newPacket;
+ peerProxy->lastPacket = newPacket;
+
+ peerProxyPrepareOutgoingHeader(peerProxy);
+ } else {
+ peerProxy->lastPacket->nextPacket = newPacket;
+ }
+}
+
+/** Takes the peer lock and enqueues the given packet. */
+static void peerProxyLockAndEnqueueOutgoingPacket(PeerProxy* peerProxy,
+ OutgoingPacket* newPacket) {
+ Peer* peer = peerProxy->peer;
+ peerLock(peer);
+ peerProxyEnqueueOutgoingPacket(peerProxy, newPacket);
+ peerUnlock(peer);
+}
+
+/**
+ * Frees current packet and moves to the next one. Returns true if there is
+ * a next packet or false if the queue is empty.
+ */
+static bool peerProxyNextPacket(PeerProxy* peerProxy) {
+ Peer* peer = peerProxy->peer;
+ peerLock(peer);
+
+ OutgoingPacket* current = peerProxy->currentPacket;
+
+ if (current == NULL) {
+ // The queue is already empty.
+ peerUnlock(peer);
+ return false;
+ }
+
+ OutgoingPacket* next = current->nextPacket;
+ peerProxy->currentPacket = next;
+ current->nextPacket = NULL;
+ current->free(current);
+ if (next == NULL) {
+ // The queue is empty.
+ peerProxy->lastPacket = NULL;
+ peerUnlock(peer);
+ return false;
+ } else {
+ peerUnlock(peer);
+ peerProxyPrepareOutgoingHeader(peerProxy);
+
+ // TODO: Start writing next packet? It would reduce the number of
+ // system calls, but we could also starve other peers.
+ return true;
+ }
+}
+
+/**
+ * Checks whether a peer died recently.
+ */
+static bool peerIsDead(Peer* peer, pid_t pid) {
+ size_t i;
+ for (i = 0; i < PEER_HISTORY; i++) {
+ pid_t deadPeer = peer->deadPeers[i];
+ if (deadPeer == 0) {
+ return false;
+ }
+ if (deadPeer == pid) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Cleans up connection information.
+ */
+static bool peerProxyRemoveConnection(void* key, void* value, void* context) {
+ PeerProxy* deadPeer = (PeerProxy*) context;
+ PeerProxy* otherPeer = (PeerProxy*) value;
+ hashmapRemove(otherPeer->connections, &(deadPeer->credentials.pid));
+ return true;
+}
+
+/**
+ * Called when the peer dies.
+ */
+static void peerProxyKill(PeerProxy* peerProxy, bool errnoIsSet) {
+ if (errnoIsSet) {
+ LOGI("Peer %d died. errno: %s", peerProxy->credentials.pid,
+ strerror(errno));
+ } else {
+ LOGI("Peer %d died.", peerProxy->credentials.pid);
+ }
+
+ // If we lost the master, we're up a creek. We can't let this happen.
+ if (peerProxy->master) {
+ LOG_ALWAYS_FATAL("Lost connection to master.");
+ }
+
+ Peer* localPeer = peerProxy->peer;
+ pid_t pid = peerProxy->credentials.pid;
+
+ peerLock(localPeer);
+
+ // Remember for awhile that the peer died.
+ localPeer->deadPeers[localPeer->deadPeerCursor]
+ = peerProxy->credentials.pid;
+ localPeer->deadPeerCursor++;
+ if (localPeer->deadPeerCursor == PEER_HISTORY) {
+ localPeer->deadPeerCursor = 0;
+ }
+
+ // Remove from peer map.
+ hashmapRemove(localPeer->peerProxies, &pid);
+
+ // External threads can no longer get to this peer proxy, so we don't
+ // need the lock anymore.
+ peerUnlock(localPeer);
+
+ // Remove the fd from the selector.
+ if (peerProxy->fd != NULL) {
+ peerProxy->fd->remove = true;
+ }
+
+ // Clear outgoing packet queue.
+ while (peerProxyNextPacket(peerProxy)) {}
+
+ bufferFree(peerProxy->inputBuffer);
+
+ // This only applies to the master.
+ if (peerProxy->connections != NULL) {
+ // We can't leave these other maps pointing to freed memory.
+ hashmapForEach(peerProxy->connections, &peerProxyRemoveConnection,
+ peerProxy);
+ hashmapFree(peerProxy->connections);
+ }
+
+ // Invoke death listener.
+ localPeer->onDeath(pid);
+
+ // Free the peer proxy itself.
+ free(peerProxy);
+}
+
+static void peerProxyHandleError(PeerProxy* peerProxy, char* functionName) {
+ if (errno == EINTR) {
+ // Log interruptions but otherwise ignore them.
+ LOGW("%s() interrupted.", functionName);
+ } else if (errno == EAGAIN) {
+ LOGD("EWOULDBLOCK");
+ // Ignore.
+ } else {
+ LOGW("Error returned by %s().", functionName);
+ peerProxyKill(peerProxy, true);
+ }
+}
+
+/**
+ * Buffers output sent to a peer. May be called multiple times until the entire
+ * buffer is filled. Returns true when the buffer is empty.
+ */
+static bool peerProxyWriteFromBuffer(PeerProxy* peerProxy, Buffer* outgoing) {
+ ssize_t size = bufferWrite(outgoing, peerProxy->fd->fd);
+ if (size < 0) {
+ peerProxyHandleError(peerProxy, "write");
+ return false;
+ } else {
+ return bufferWriteComplete(outgoing);
+ }
+}
+
+/** Writes packet bytes to peer. */
+static void peerProxyWriteBytes(PeerProxy* peerProxy) {
+ Buffer* buffer = peerProxy->currentPacket->bytes;
+ if (peerProxyWriteFromBuffer(peerProxy, buffer)) {
+ LOGD("Bytes written.");
+ peerProxyNextPacket(peerProxy);
+ }
+}
+
+/** Sends a socket to the peer. */
+static void peerProxyWriteConnection(PeerProxy* peerProxy) {
+ int socket = peerProxy->currentPacket->socket;
+
+ // Why does sending and receiving fds have to be such a PITA?
+ struct msghdr msg;
+ struct iovec iov[1];
+
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+
+ struct cmsghdr *cmptr;
+
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+ cmptr = CMSG_FIRSTHDR(&msg);
+ cmptr->cmsg_len = CMSG_LEN(sizeof(int));
+ cmptr->cmsg_level = SOL_SOCKET;
+ cmptr->cmsg_type = SCM_RIGHTS;
+
+ // Store the socket in the message.
+ *((int *) CMSG_DATA(cmptr)) = peerProxy->currentPacket->socket;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ iov[0].iov_base = "";
+ iov[0].iov_len = 1;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ ssize_t result = sendmsg(peerProxy->fd->fd, &msg, 0);
+
+ if (result < 0) {
+ peerProxyHandleError(peerProxy, "sendmsg");
+ } else {
+ // Success. Queue up the next packet.
+ peerProxyNextPacket(peerProxy);
+
+ }
+}
+
+/**
+ * Writes some outgoing data.
+ */
+static void peerProxyWrite(SelectableFd* fd) {
+ // TODO: Try to write header and body with one system call.
+
+ PeerProxy* peerProxy = (PeerProxy*) fd->data;
+ OutgoingPacket* current = peerProxy->currentPacket;
+
+ if (current == NULL) {
+ // We have nothing left to write.
+ return;
+ }
+
+ // Write the header.
+ Buffer* outgoingHeader = &peerProxy->outgoingHeader;
+ bool headerWritten = bufferWriteComplete(outgoingHeader);
+ if (!headerWritten) {
+ LOGD("Writing header...");
+ headerWritten = peerProxyWriteFromBuffer(peerProxy, outgoingHeader);
+ if (headerWritten) {
+ LOGD("Header written.");
+ }
+ }
+
+ // Write body.
+ if (headerWritten) {
+ PacketType type = current->header.type;
+ switch (type) {
+ case CONNECTION:
+ peerProxyWriteConnection(peerProxy);
+ break;
+ case BYTES:
+ peerProxyWriteBytes(peerProxy);
+ break;
+ case CONNECTION_REQUEST:
+ case CONNECTION_ERROR:
+ // These packets consist solely of a header.
+ peerProxyNextPacket(peerProxy);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown packet type: %d", type);
+ }
+ }
+}
+
+/**
+ * Sets up a peer proxy's fd before we try to select() it.
+ */
+static void peerProxyBeforeSelect(SelectableFd* fd) {
+ LOGD("Before select...");
+
+ PeerProxy* peerProxy = (PeerProxy*) fd->data;
+
+ peerLock(peerProxy->peer);
+ bool hasPackets = peerProxy->currentPacket != NULL;
+ peerUnlock(peerProxy->peer);
+
+ if (hasPackets) {
+ LOGD("Packets found. Setting onWritable().");
+
+ fd->onWritable = &peerProxyWrite;
+ } else {
+ // We have nothing to write.
+ fd->onWritable = NULL;
+ }
+}
+
+/** Prepare to read bytes from the peer. */
+static void peerProxyExpectBytes(PeerProxy* peerProxy, Header* header) {
+ LOGD("Expecting %d bytes.", header->size);
+
+ peerProxy->inputState = READING_BYTES;
+ if (bufferPrepareForRead(peerProxy->inputBuffer, header->size) == -1) {
+ LOGW("Couldn't allocate memory for incoming data. Size: %u",
+ (unsigned int) header->size);
+
+ // TODO: Ignore the packet and log a warning?
+ peerProxyKill(peerProxy, false);
+ }
+}
+
+/**
+ * Gets a peer proxy for the given ID. Creates a peer proxy if necessary.
+ * Sends a connection request to the master if desired.
+ *
+ * Returns NULL if an error occurs. Sets errno to EHOSTDOWN if the peer died
+ * or ENOMEM if memory couldn't be allocated.
+ */
+static PeerProxy* peerProxyGetOrCreate(Peer* peer, pid_t pid,
+ bool requestConnection) {
+ if (pid == peer->pid) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (peerIsDead(peer, pid)) {
+ errno = EHOSTDOWN;
+ return NULL;
+ }
+
+ PeerProxy* peerProxy = hashmapGet(peer->peerProxies, &pid);
+ if (peerProxy != NULL) {
+ return peerProxy;
+ }
+
+ // If this is the master peer, we already know about all peers.
+ if (peer->master) {
+ errno = EHOSTDOWN;
+ return NULL;
+ }
+
+ // Try to create a peer proxy.
+ Credentials credentials;
+ credentials.pid = pid;
+
+ // Fake gid and uid until we have the real thing. The real creds are
+ // filled in by masterProxyExpectConnection(). These fake creds will
+ // never be exposed to the user.
+ credentials.uid = 0;
+ credentials.gid = 0;
+
+ // Make sure we can allocate the connection request packet.
+ OutgoingPacket* packet = NULL;
+ if (requestConnection) {
+ packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ packet->header.type = CONNECTION_REQUEST;
+ packet->header.credentials = credentials;
+ packet->free = &outgoingPacketFree;
+ }
+
+ peerProxy = peerProxyCreate(peer, credentials);
+ if (peerProxy == NULL) {
+ free(packet);
+ errno = ENOMEM;
+ return NULL;
+ } else {
+ // Send a connection request to the master.
+ if (requestConnection) {
+ PeerProxy* masterProxy = peer->masterProxy;
+ peerProxyEnqueueOutgoingPacket(masterProxy, packet);
+ }
+
+ return peerProxy;
+ }
+}
+
+/**
+ * Switches the master peer proxy into a state where it's waiting for a
+ * connection from the master.
+ */
+static void masterProxyExpectConnection(PeerProxy* masterProxy,
+ Header* header) {
+ // TODO: Restructure things so we don't need this check.
+ // Verify that this really is the master.
+ if (!masterProxy->master) {
+ LOGW("Non-master process %d tried to send us a connection.",
+ masterProxy->credentials.pid);
+ // Kill off the evil peer.
+ peerProxyKill(masterProxy, false);
+ return;
+ }
+
+ masterProxy->inputState = ACCEPTING_CONNECTION;
+ Peer* localPeer = masterProxy->peer;
+
+ // Create a peer proxy so we have somewhere to stash the creds.
+ // See if we already have a proxy set up.
+ pid_t pid = header->credentials.pid;
+ peerLock(localPeer);
+ PeerProxy* peerProxy = peerProxyGetOrCreate(localPeer, pid, false);
+ if (peerProxy == NULL) {
+ LOGW("Peer proxy creation failed: %s", strerror(errno));
+ } else {
+ // Fill in full credentials.
+ peerProxy->credentials = header->credentials;
+ }
+ peerUnlock(localPeer);
+
+ // Keep track of which peer proxy we're accepting a connection for.
+ masterProxy->connecting = peerProxy;
+}
+
+/**
+ * Reads input from a peer process.
+ */
+static void peerProxyRead(SelectableFd* fd);
+
+/** Sets up fd callbacks. */
+static void peerProxySetFd(PeerProxy* peerProxy, SelectableFd* fd) {
+ peerProxy->fd = fd;
+ fd->data = peerProxy;
+ fd->onReadable = &peerProxyRead;
+ fd->beforeSelect = &peerProxyBeforeSelect;
+
+ // Make the socket non-blocking.
+ setNonBlocking(fd->fd);
+}
+
+/**
+ * Accepts a connection sent by the master proxy.
+ */
+static void masterProxyAcceptConnection(PeerProxy* masterProxy) {
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t size;
+ char ignored;
+ int incomingFd;
+
+ // TODO: Reuse code which writes the connection. Who the heck designed
+ // this API anyway?
+ union {
+ struct cmsghdr cm;
+ char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+ struct cmsghdr *cmptr;
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof(control_un.control);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ // We sent 1 byte of data so we can detect EOF.
+ iov[0].iov_base = &ignored;
+ iov[0].iov_len = 1;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ size = recvmsg(masterProxy->fd->fd, &msg, 0);
+ if (size < 0) {
+ if (errno == EINTR) {
+ // Log interruptions but otherwise ignore them.
+ LOGW("recvmsg() interrupted.");
+ return;
+ } else if (errno == EAGAIN) {
+ // Keep waiting for the connection.
+ return;
+ } else {
+ LOG_ALWAYS_FATAL("Error reading connection from master: %s",
+ strerror(errno));
+ }
+ } else if (size == 0) {
+ // EOF.
+ LOG_ALWAYS_FATAL("Received EOF from master.");
+ }
+
+ // Extract fd from message.
+ if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL
+ && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
+ if (cmptr->cmsg_level != SOL_SOCKET) {
+ LOG_ALWAYS_FATAL("Expected SOL_SOCKET.");
+ }
+ if (cmptr->cmsg_type != SCM_RIGHTS) {
+ LOG_ALWAYS_FATAL("Expected SCM_RIGHTS.");
+ }
+ incomingFd = *((int*) CMSG_DATA(cmptr));
+ } else {
+ LOG_ALWAYS_FATAL("Expected fd.");
+ }
+
+ // The peer proxy this connection is for.
+ PeerProxy* peerProxy = masterProxy->connecting;
+ if (peerProxy == NULL) {
+ LOGW("Received connection for unknown peer.");
+ closeWithWarning(incomingFd);
+ } else {
+ Peer* peer = masterProxy->peer;
+
+ SelectableFd* selectableFd = selectorAdd(peer->selector, incomingFd);
+ if (selectableFd == NULL) {
+ LOGW("Error adding fd to selector for %d.",
+ peerProxy->credentials.pid);
+ closeWithWarning(incomingFd);
+ peerProxyKill(peerProxy, false);
+ }
+
+ peerProxySetFd(peerProxy, selectableFd);
+ }
+
+ peerProxyExpectHeader(masterProxy);
+}
+
+/**
+ * Frees an outgoing packet containing a connection.
+ */
+static void outgoingPacketFreeSocket(OutgoingPacket* packet) {
+ closeWithWarning(packet->socket);
+ outgoingPacketFree(packet);
+}
+
+/**
+ * Connects two known peers.
+ */
+static void masterConnectPeers(PeerProxy* peerA, PeerProxy* peerB) {
+ int sockets[2];
+ int result = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets);
+ if (result == -1) {
+ LOGW("socketpair() error: %s", strerror(errno));
+ // TODO: Send CONNECTION_FAILED packets to peers.
+ return;
+ }
+
+ OutgoingPacket* packetA = calloc(1, sizeof(OutgoingPacket));
+ OutgoingPacket* packetB = calloc(1, sizeof(OutgoingPacket));
+ if (packetA == NULL || packetB == NULL) {
+ free(packetA);
+ free(packetB);
+ LOGW("malloc() error. Failed to tell process %d that process %d is"
+ " dead.", peerA->credentials.pid, peerB->credentials.pid);
+ return;
+ }
+
+ packetA->header.type = CONNECTION;
+ packetB->header.type = CONNECTION;
+
+ packetA->header.credentials = peerB->credentials;
+ packetB->header.credentials = peerA->credentials;
+
+ packetA->socket = sockets[0];
+ packetB->socket = sockets[1];
+
+ packetA->free = &outgoingPacketFreeSocket;
+ packetB->free = &outgoingPacketFreeSocket;
+
+ peerLock(peerA->peer);
+ peerProxyEnqueueOutgoingPacket(peerA, packetA);
+ peerProxyEnqueueOutgoingPacket(peerB, packetB);
+ peerUnlock(peerA->peer);
+}
+
+/**
+ * Informs a peer that the peer they're trying to connect to couldn't be
+ * found.
+ */
+static void masterReportConnectionError(PeerProxy* peerProxy,
+ Credentials credentials) {
+ OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ LOGW("malloc() error. Failed to tell process %d that process %d is"
+ " dead.", peerProxy->credentials.pid, credentials.pid);
+ return;
+ }
+
+ packet->header.type = CONNECTION_ERROR;
+ packet->header.credentials = credentials;
+ packet->free = &outgoingPacketFree;
+
+ peerProxyLockAndEnqueueOutgoingPacket(peerProxy, packet);
+}
+
+/**
+ * Handles a request to be connected to another peer.
+ */
+static void masterHandleConnectionRequest(PeerProxy* peerProxy,
+ Header* header) {
+ Peer* master = peerProxy->peer;
+ pid_t targetPid = header->credentials.pid;
+ if (!hashmapContainsKey(peerProxy->connections, &targetPid)) {
+ // We haven't connected these peers yet.
+ PeerProxy* targetPeer
+ = (PeerProxy*) hashmapGet(master->peerProxies, &targetPid);
+ if (targetPeer == NULL) {
+ // Unknown process.
+ masterReportConnectionError(peerProxy, header->credentials);
+ } else {
+ masterConnectPeers(peerProxy, targetPeer);
+ }
+ }
+
+ // This packet is complete. Get ready for the next one.
+ peerProxyExpectHeader(peerProxy);
+}
+
+/**
+ * The master told us this peer is dead.
+ */
+static void masterProxyHandleConnectionError(PeerProxy* masterProxy,
+ Header* header) {
+ Peer* peer = masterProxy->peer;
+
+ // Look up the peer proxy.
+ pid_t pid = header->credentials.pid;
+ PeerProxy* peerProxy = NULL;
+ peerLock(peer);
+ peerProxy = hashmapGet(peer->peerProxies, &pid);
+ peerUnlock(peer);
+
+ if (peerProxy != NULL) {
+ LOGI("Couldn't connect to %d.", pid);
+ peerProxyKill(peerProxy, false);
+ } else {
+ LOGW("Peer proxy for %d not found. This shouldn't happen.", pid);
+ }
+
+ peerProxyExpectHeader(masterProxy);
+}
+
+/**
+ * Handles a packet header.
+ */
+static void peerProxyHandleHeader(PeerProxy* peerProxy, Header* header) {
+ switch (header->type) {
+ case CONNECTION_REQUEST:
+ masterHandleConnectionRequest(peerProxy, header);
+ break;
+ case CONNECTION:
+ masterProxyExpectConnection(peerProxy, header);
+ break;
+ case CONNECTION_ERROR:
+ masterProxyHandleConnectionError(peerProxy, header);
+ break;
+ case BYTES:
+ peerProxyExpectBytes(peerProxy, header);
+ break;
+ default:
+ LOGW("Invalid packet type from %d: %d", peerProxy->credentials.pid,
+ header->type);
+ peerProxyKill(peerProxy, false);
+ }
+}
+
+/**
+ * Buffers input sent by peer. May be called multiple times until the entire
+ * buffer is filled. Returns true when the buffer is full.
+ */
+static bool peerProxyBufferInput(PeerProxy* peerProxy) {
+ Buffer* in = peerProxy->inputBuffer;
+ ssize_t size = bufferRead(in, peerProxy->fd->fd);
+ if (size < 0) {
+ peerProxyHandleError(peerProxy, "read");
+ return false;
+ } else if (size == 0) {
+ // EOF.
+ LOGI("EOF");
+ peerProxyKill(peerProxy, false);
+ return false;
+ } else if (bufferReadComplete(in)) {
+ // We're done!
+ return true;
+ } else {
+ // Continue reading.
+ return false;
+ }
+}
+
+/**
+ * Reads input from a peer process.
+ */
+static void peerProxyRead(SelectableFd* fd) {
+ LOGD("Reading...");
+ PeerProxy* peerProxy = (PeerProxy*) fd->data;
+ int state = peerProxy->inputState;
+ Buffer* in = peerProxy->inputBuffer;
+ switch (state) {
+ case READING_HEADER:
+ if (peerProxyBufferInput(peerProxy)) {
+ LOGD("Header read.");
+ // We've read the complete header.
+ Header* header = (Header*) in->data;
+ peerProxyHandleHeader(peerProxy, header);
+ }
+ break;
+ case READING_BYTES:
+ LOGD("Reading bytes...");
+ if (peerProxyBufferInput(peerProxy)) {
+ LOGD("Bytes read.");
+ // We have the complete packet. Notify bytes listener.
+ peerProxy->peer->onBytes(peerProxy->credentials,
+ in->data, in->size);
+
+ // Get ready for the next packet.
+ peerProxyExpectHeader(peerProxy);
+ }
+ break;
+ case ACCEPTING_CONNECTION:
+ masterProxyAcceptConnection(peerProxy);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unknown state: %d", state);
+ }
+}
+
+static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials) {
+ PeerProxy* peerProxy = calloc(1, sizeof(PeerProxy));
+ if (peerProxy == NULL) {
+ return NULL;
+ }
+
+ peerProxy->inputBuffer = bufferCreate(sizeof(Header));
+ if (peerProxy->inputBuffer == NULL) {
+ free(peerProxy);
+ return NULL;
+ }
+
+ peerProxy->peer = peer;
+ peerProxy->credentials = credentials;
+
+ // Initial state == expecting a header.
+ peerProxyExpectHeader(peerProxy);
+
+ // Add this proxy to the map. Make sure the key points to the stable memory
+ // inside of the peer proxy itself.
+ pid_t* pid = &(peerProxy->credentials.pid);
+ hashmapPut(peer->peerProxies, pid, peerProxy);
+ return peerProxy;
+}
+
+/** Accepts a connection to the master peer. */
+static void masterAcceptConnection(SelectableFd* listenerFd) {
+ // Accept connection.
+ int socket = accept(listenerFd->fd, NULL, NULL);
+ if (socket == -1) {
+ LOGW("accept() error: %s", strerror(errno));
+ return;
+ }
+
+ LOGD("Accepted connection as fd %d.", socket);
+
+ // Get credentials.
+ Credentials credentials;
+ struct ucred ucredentials;
+ socklen_t credentialsSize = sizeof(struct ucred);
+ int result = getsockopt(socket, SOL_SOCKET, SO_PEERCRED,
+ &ucredentials, &credentialsSize);
+ // We might want to verify credentialsSize.
+ if (result == -1) {
+ LOGW("getsockopt() error: %s", strerror(errno));
+ closeWithWarning(socket);
+ return;
+ }
+
+ // Copy values into our own structure so we know we have the types right.
+ credentials.pid = ucredentials.pid;
+ credentials.uid = ucredentials.uid;
+ credentials.gid = ucredentials.gid;
+
+ LOGI("Accepted connection from process %d.", credentials.pid);
+
+ Peer* masterPeer = (Peer*) listenerFd->data;
+
+ peerLock(masterPeer);
+
+ // Make sure we don't already have a connection from that process.
+ PeerProxy* peerProxy
+ = hashmapGet(masterPeer->peerProxies, &credentials.pid);
+ if (peerProxy != NULL) {
+ peerUnlock(masterPeer);
+ LOGW("Alread connected to process %d.", credentials.pid);
+ closeWithWarning(socket);
+ return;
+ }
+
+ // Add connection to the selector.
+ SelectableFd* socketFd = selectorAdd(masterPeer->selector, socket);
+ if (socketFd == NULL) {
+ peerUnlock(masterPeer);
+ LOGW("malloc() failed.");
+ closeWithWarning(socket);
+ return;
+ }
+
+ // Create a peer proxy.
+ peerProxy = peerProxyCreate(masterPeer, credentials);
+ peerUnlock(masterPeer);
+ if (peerProxy == NULL) {
+ LOGW("malloc() failed.");
+ socketFd->remove = true;
+ closeWithWarning(socket);
+ }
+ peerProxy->connections = hashmapCreate(10, &pidHash, &pidEquals);
+ peerProxySetFd(peerProxy, socketFd);
+}
+
+/**
+ * Creates the local peer.
+ */
+static Peer* peerCreate() {
+ Peer* peer = calloc(1, sizeof(Peer));
+ if (peer == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ peer->peerProxies = hashmapCreate(10, &pidHash, &pidEquals);
+ peer->selector = selectorCreate();
+
+ pthread_mutexattr_t attributes;
+ if (pthread_mutexattr_init(&attributes) != 0) {
+ LOG_ALWAYS_FATAL("pthread_mutexattr_init() error.");
+ }
+ if (pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE) != 0) {
+ LOG_ALWAYS_FATAL("pthread_mutexattr_settype() error.");
+ }
+ if (pthread_mutex_init(&peer->mutex, &attributes) != 0) {
+ LOG_ALWAYS_FATAL("pthread_mutex_init() error.");
+ }
+
+ peer->pid = getpid();
+ return peer;
+}
+
+/** The local peer. */
+static Peer* localPeer;
+
+/** Frees a packet of bytes. */
+static void outgoingPacketFreeBytes(OutgoingPacket* packet) {
+ LOGD("Freeing outgoing packet.");
+ bufferFree(packet->bytes);
+ free(packet);
+}
+
+/**
+ * Sends a packet of bytes to a remote peer. Returns 0 on success.
+ *
+ * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be
+ * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno
+ * to EINVAL if pid is the same as the local pid.
+ */
+int peerSendBytes(pid_t pid, const char* bytes, size_t size) {
+ Peer* peer = localPeer;
+ assert(peer != NULL);
+
+ OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ Buffer* copy = bufferCreate(size);
+ if (copy == NULL) {
+ free(packet);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // Copy data.
+ memcpy(copy->data, bytes, size);
+ copy->size = size;
+
+ packet->bytes = copy;
+ packet->header.type = BYTES;
+ packet->header.size = size;
+ packet->free = outgoingPacketFreeBytes;
+ bufferPrepareForWrite(packet->bytes);
+
+ peerLock(peer);
+
+ PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true);
+ if (peerProxy == NULL) {
+ // The peer is already dead or we couldn't alloc memory. Either way,
+ // errno is set.
+ peerUnlock(peer);
+ packet->free(packet);
+ return -1;
+ } else {
+ peerProxyEnqueueOutgoingPacket(peerProxy, packet);
+ peerUnlock(peer);
+ selectorWakeUp(peer->selector);
+ return 0;
+ }
+}
+
+/** Keeps track of how to free shared bytes. */
+typedef struct {
+ void (*free)(void* context);
+ void* context;
+} SharedBytesFreer;
+
+/** Frees shared bytes. */
+static void outgoingPacketFreeSharedBytes(OutgoingPacket* packet) {
+ SharedBytesFreer* sharedBytesFreer
+ = (SharedBytesFreer*) packet->context;
+ sharedBytesFreer->free(sharedBytesFreer->context);
+ free(sharedBytesFreer);
+ free(packet);
+}
+
+/**
+ * Sends a packet of bytes to a remote peer without copying the bytes. Calls
+ * free() with context after the bytes have been sent.
+ *
+ * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be
+ * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno
+ * to EINVAL if pid is the same as the local pid.
+ */
+int peerSendSharedBytes(pid_t pid, char* bytes, size_t size,
+ void (*free)(void* context), void* context) {
+ Peer* peer = localPeer;
+ assert(peer != NULL);
+
+ OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket));
+ if (packet == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ Buffer* wrapper = bufferWrap(bytes, size, size);
+ if (wrapper == NULL) {
+ free(packet);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ SharedBytesFreer* sharedBytesFreer = malloc(sizeof(SharedBytesFreer));
+ if (sharedBytesFreer == NULL) {
+ free(packet);
+ free(wrapper);
+ errno = ENOMEM;
+ return -1;
+ }
+ sharedBytesFreer->free = free;
+ sharedBytesFreer->context = context;
+
+ packet->bytes = wrapper;
+ packet->context = sharedBytesFreer;
+ packet->header.type = BYTES;
+ packet->header.size = size;
+ packet->free = &outgoingPacketFreeSharedBytes;
+ bufferPrepareForWrite(packet->bytes);
+
+ peerLock(peer);
+
+ PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true);
+ if (peerProxy == NULL) {
+ // The peer is already dead or we couldn't alloc memory. Either way,
+ // errno is set.
+ peerUnlock(peer);
+ packet->free(packet);
+ return -1;
+ } else {
+ peerProxyEnqueueOutgoingPacket(peerProxy, packet);
+ peerUnlock(peer);
+ selectorWakeUp(peer->selector);
+ return 0;
+ }
+}
+
+/**
+ * Starts the master peer. The master peer differs from other peers in that
+ * it is responsible for connecting the other peers. You can only have one
+ * master peer.
+ *
+ * Goes into an I/O loop and does not return.
+ */
+void masterPeerInitialize(BytesListener* bytesListener,
+ DeathListener* deathListener) {
+ // Create and bind socket.
+ int listenerSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (listenerSocket == -1) {
+ LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno));
+ }
+ unlink(MASTER_PATH);
+ int result = bind(listenerSocket, (SocketAddress*) getMasterAddress(),
+ sizeof(UnixAddress));
+ if (result == -1) {
+ LOG_ALWAYS_FATAL("bind() error: %s", strerror(errno));
+ }
+
+ LOGD("Listener socket: %d", listenerSocket);
+
+ // Queue up to 16 connections.
+ result = listen(listenerSocket, 16);
+ if (result != 0) {
+ LOG_ALWAYS_FATAL("listen() error: %s", strerror(errno));
+ }
+
+ // Make socket non-blocking.
+ setNonBlocking(listenerSocket);
+
+ // Create the peer for this process. Fail if we already have one.
+ if (localPeer != NULL) {
+ LOG_ALWAYS_FATAL("Peer is already initialized.");
+ }
+ localPeer = peerCreate();
+ if (localPeer == NULL) {
+ LOG_ALWAYS_FATAL("malloc() failed.");
+ }
+ localPeer->master = true;
+ localPeer->onBytes = bytesListener;
+ localPeer->onDeath = deathListener;
+
+ // Make listener socket selectable.
+ SelectableFd* listenerFd = selectorAdd(localPeer->selector, listenerSocket);
+ if (listenerFd == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ listenerFd->data = localPeer;
+ listenerFd->onReadable = &masterAcceptConnection;
+}
+
+/**
+ * Starts a local peer.
+ *
+ * Goes into an I/O loop and does not return.
+ */
+void peerInitialize(BytesListener* bytesListener,
+ DeathListener* deathListener) {
+ // Connect to master peer.
+ int masterSocket = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (masterSocket == -1) {
+ LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno));
+ }
+ int result = connect(masterSocket, (SocketAddress*) getMasterAddress(),
+ sizeof(UnixAddress));
+ if (result != 0) {
+ LOG_ALWAYS_FATAL("connect() error: %s", strerror(errno));
+ }
+
+ // Create the peer for this process. Fail if we already have one.
+ if (localPeer != NULL) {
+ LOG_ALWAYS_FATAL("Peer is already initialized.");
+ }
+ localPeer = peerCreate();
+ if (localPeer == NULL) {
+ LOG_ALWAYS_FATAL("malloc() failed.");
+ }
+ localPeer->onBytes = bytesListener;
+ localPeer->onDeath = deathListener;
+
+ // Make connection selectable.
+ SelectableFd* masterFd = selectorAdd(localPeer->selector, masterSocket);
+ if (masterFd == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+
+ // Create a peer proxy for the master peer.
+ PeerProxy* masterProxy = peerProxyCreate(localPeer, MASTER_CREDENTIALS);
+ if (masterProxy == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ peerProxySetFd(masterProxy, masterFd);
+ masterProxy->master = true;
+ localPeer->masterProxy = masterProxy;
+}
+
+/** Starts the master peer I/O loop. Doesn't return. */
+void peerLoop() {
+ assert(localPeer != NULL);
+
+ // Start selector.
+ selectorLoop(localPeer->selector);
+}
+
diff --git a/libcutils/mspace.c b/libcutils/mspace.c
new file mode 100644
index 0000000..8fd5de7
--- /dev/null
+++ b/libcutils/mspace.c
@@ -0,0 +1,246 @@
+/* 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_name(size_t starting_capacity,
+ size_t max_capacity, int locked, char const * name) {
+ int fd, ret;
+ struct mspace_contig_state *cs;
+ 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);
+
+ /* Reserve some space for the information that our MORECORE needs.
+ */
+ cs = base;
+
+ /* Create the mspace, pointing to the memory we just reserved.
+ */
+ m = create_mspace_with_base(base + sizeof(*cs), starting_capacity, locked);
+ if (m == (mspace)0)
+ goto error;
+
+ /* Make sure that m is in the same page as cs.
+ */
+ assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)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(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)
+ goto error;
+ }
+
+ cs->m = m;
+ cs->magic = CONTIG_STATE_MAGIC;
+
+ return (mspace)m;
+
+error:
+ munmap(base, max_capacity);
+ return (mspace)0;
+}
+
+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;
+}
+#endif
diff --git a/libcutils/private.h b/libcutils/private.h
new file mode 100644
index 0000000..2837b70
--- /dev/null
+++ b/libcutils/private.h
@@ -0,0 +1,368 @@
+#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
new file mode 100644
index 0000000..17f52e2
--- /dev/null
+++ b/libcutils/process_name.c
@@ -0,0 +1,75 @@
+/*
+ * 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 <string.h>
+#include <cutils/process_name.h>
+#include <cutils/properties.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
+
+static const char* process_name = "unknown";
+static int running_in_emulator = -1;
+
+void set_process_name(const char* new_name) {
+ char propBuf[PROPERTY_VALUE_MAX];
+
+ if (new_name == NULL) {
+ return;
+ }
+
+ // We never free the old name. Someone else could be using it.
+ char* copy = (char*) malloc(strlen(new_name) + 1);
+ strcpy(copy, new_name);
+ process_name = (const char*) copy;
+
+ // If we know we are not running in the emulator, then return.
+ if (running_in_emulator == 0) {
+ return;
+ }
+
+ // If the "running_in_emulator" variable has not been initialized,
+ // then do it now.
+ if (running_in_emulator == -1) {
+ property_get("ro.kernel.qemu", propBuf, "");
+ if (propBuf[0] == '1') {
+ running_in_emulator = 1;
+ } else {
+ running_in_emulator = 0;
+ return;
+ }
+ }
+
+ // If the emulator was started with the "-trace file" command line option
+ // then we want to record the process name in the trace even if we are
+ // not currently tracing instructions (so that we will know the process
+ // name when we do start tracing instructions). We do not need to execute
+ // this code if we are just running in the emulator without the "-trace"
+ // command line option, but we don't know that here and this function
+ // isn't called frequently enough to bother optimizing that case.
+ int fd = open(PROCESS_NAME_DEVICE, O_RDWR);
+ if (fd < 0)
+ return;
+ write(fd, process_name, strlen(process_name) + 1);
+ close(fd);
+}
+
+const char* get_process_name(void) {
+ return process_name;
+}
diff --git a/libcutils/properties.c b/libcutils/properties.c
new file mode 100644
index 0000000..547cc6d
--- /dev/null
+++ b/libcutils/properties.c
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "properties"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <cutils/properties.h>
+#include "loghack.h"
+
+#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+static int send_prop_msg(prop_msg *msg)
+{
+ int s;
+ int r;
+
+ s = socket_local_client(PROP_SERVICE_NAME,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if(s < 0) return -1;
+
+ while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {
+ if((errno == EINTR) || (errno == EAGAIN)) continue;
+ break;
+ }
+
+ if(r == sizeof(prop_msg)) {
+ r = 0;
+ } else {
+ r = -1;
+ }
+
+ close(s);
+ return r;
+}
+
+int property_set(const char *key, const char *value)
+{
+ prop_msg msg;
+ unsigned resp;
+
+ if(key == 0) return -1;
+ if(value == 0) value = "";
+
+ if(strlen(key) >= PROP_NAME_MAX) return -1;
+ if(strlen(value) >= PROP_VALUE_MAX) return -1;
+
+ msg.cmd = PROP_MSG_SETPROP;
+ strcpy((char*) msg.name, key);
+ strcpy((char*) msg.value, value);
+
+ return send_prop_msg(&msg);
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ int len;
+
+ len = __system_property_get(key, value);
+ if(len > 0) {
+ return len;
+ }
+
+ if(default_value) {
+ len = strlen(default_value);
+ memcpy(value, default_value, len + 1);
+ }
+ return len;
+}
+
+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;
+}
+
+#elif defined(HAVE_SYSTEM_PROPERTY_SERVER)
+
+/*
+ * The Linux simulator provides a "system property server" that uses IPC
+ * to set/get/list properties. The file descriptor is shared by all
+ * threads in the process, so we use a mutex to ensure that requests
+ * from multiple threads don't get interleaved.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pthread.h>
+
+static pthread_once_t gInitOnce = PTHREAD_ONCE_INIT;
+static pthread_mutex_t gPropertyFdLock = PTHREAD_MUTEX_INITIALIZER;
+static int gPropFd = -1;
+
+/*
+ * Connect to the properties server.
+ *
+ * Returns the socket descriptor on success.
+ */
+static int connectToServer(const char* fileName)
+{
+ int sock = -1;
+ int cc;
+
+ struct sockaddr_un addr;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ LOGW("UNIX domain socket create failed (errno=%d)\n", errno);
+ return -1;
+ }
+
+ /* connect to socket; fails if file doesn't exist */
+ strcpy(addr.sun_path, fileName); // max 108 bytes
+ addr.sun_family = AF_UNIX;
+ cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+ if (cc < 0) {
+ // ENOENT means socket file doesn't exist
+ // ECONNREFUSED means socket exists but nobody is listening
+ //LOGW("AF_UNIX connect failed for '%s': %s\n",
+ // fileName, strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+/*
+ * Perform one-time initialization.
+ */
+static void init(void)
+{
+ assert(gPropFd == -1);
+
+ gPropFd = connectToServer(SYSTEM_PROPERTY_PIPE_NAME);
+ if (gPropFd < 0) {
+ //LOGW("not connected to system property server\n");
+ } else {
+ //LOGV("Connected to system property server\n");
+ }
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ char sendBuf[1+PROPERTY_KEY_MAX];
+ char recvBuf[1+PROPERTY_VALUE_MAX];
+ int len = -1;
+
+ //LOGV("PROPERTY GET [%s]\n", key);
+
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0) {
+ /* this mimics the behavior of the device implementation */
+ if (default_value != NULL) {
+ strcpy(value, default_value);
+ len = strlen(value);
+ }
+ return len;
+ }
+
+ if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+
+ memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
+
+ sendBuf[0] = (char) kSystemPropertyGet;
+ strcpy(sendBuf+1, key);
+
+ pthread_mutex_lock(&gPropertyFdLock);
+ if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ pthread_mutex_unlock(&gPropertyFdLock);
+
+ /* first byte is 0 if value not defined, 1 if found */
+ if (recvBuf[0] == 0) {
+ if (default_value != NULL) {
+ strcpy(value, default_value);
+ len = strlen(value);
+ } else {
+ /*
+ * If the value isn't defined, hand back an empty string and
+ * a zero length, rather than a failure. This seems wrong,
+ * since you can't tell the difference between "undefined" and
+ * "defined but empty", but it's what the device does.
+ */
+ value[0] = '\0';
+ len = 0;
+ }
+ } else if (recvBuf[0] == 1) {
+ strcpy(value, recvBuf+1);
+ len = strlen(value);
+ } else {
+ LOGE("Got strange response to property_get request (%d)\n",
+ recvBuf[0]);
+ assert(0);
+ return -1;
+ }
+ //LOGV("PROP [found=%d def='%s'] (%d) [%s]: [%s]\n",
+ // recvBuf[0], default_value, len, key, value);
+
+ return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+ char sendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX];
+ char recvBuf[1];
+ int result = -1;
+
+ //LOGV("PROPERTY SET [%s]: [%s]\n", key, value);
+
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0)
+ return -1;
+
+ if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+ if (strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+
+ memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
+
+ sendBuf[0] = (char) kSystemPropertySet;
+ strcpy(sendBuf+1, key);
+ strcpy(sendBuf+1+PROPERTY_KEY_MAX, value);
+
+ pthread_mutex_lock(&gPropertyFdLock);
+ if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ pthread_mutex_unlock(&gPropertyFdLock);
+
+ if (recvBuf[0] != 1)
+ return -1;
+ return 0;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ //LOGV("PROPERTY LIST\n");
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0)
+ return -1;
+
+ return 0;
+}
+
+#else
+
+/* SUPER-cheesy place-holder implementation for Win32 */
+
+#include <cutils/threads.h>
+
+static mutex_t env_lock = MUTEX_INITIALIZER;
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ char ename[PROPERTY_KEY_MAX + 6];
+ char *p;
+ int len;
+
+ len = strlen(key);
+ if(len >= PROPERTY_KEY_MAX) return -1;
+ memcpy(ename, "PROP_", 5);
+ memcpy(ename + 5, key, len + 1);
+
+ mutex_lock(&env_lock);
+
+ p = getenv(ename);
+ if(p == 0) p = "";
+ len = strlen(p);
+ if(len >= PROPERTY_VALUE_MAX) {
+ len = PROPERTY_VALUE_MAX - 1;
+ }
+
+ if((len == 0) && default_value) {
+ len = strlen(default_value);
+ memcpy(value, default_value, len + 1);
+ } else {
+ memcpy(value, p, len);
+ value[len] = 0;
+ }
+
+ mutex_unlock(&env_lock);
+
+ return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+ char ename[PROPERTY_KEY_MAX + 6];
+ char *p;
+ int len;
+ int r;
+
+ if(strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+
+ len = strlen(key);
+ if(len >= PROPERTY_KEY_MAX) return -1;
+ memcpy(ename, "PROP_", 5);
+ memcpy(ename + 5, key, len + 1);
+
+ mutex_lock(&env_lock);
+#ifdef HAVE_MS_C_RUNTIME
+ {
+ char temp[256];
+ snprintf( temp, sizeof(temp), "%s=%s", ename, value);
+ putenv(temp);
+ r = 0;
+ }
+#else
+ r = setenv(ename, value, 1);
+#endif
+ mutex_unlock(&env_lock);
+
+ return r;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ return 0;
+}
+
+#endif
diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c
new file mode 100644
index 0000000..274423b
--- /dev/null
+++ b/libcutils/record_stream.c
@@ -0,0 +1,186 @@
+/* libs/cutils/record_stream.c
+**
+** 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 <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <cutils/record_stream.h>
+#include <string.h>
+#include <stdint.h>
+#ifdef HAVE_WINSOCK
+#include <winsock2.h> /* for ntohl */
+#else
+#include <netinet/in.h>
+#endif
+
+#define HEADER_SIZE 4
+
+struct RecordStream {
+ int fd;
+ size_t maxRecordLen;
+
+ unsigned char *buffer;
+
+ unsigned char *unconsumed;
+ unsigned char *read_end;
+ unsigned char *buffer_end;
+};
+
+
+extern RecordStream *record_stream_new(int fd, size_t maxRecordLen)
+{
+ RecordStream *ret;
+
+ assert (maxRecordLen <= 0xffff);
+
+ ret = (RecordStream *)calloc(1, sizeof(RecordStream));
+
+ ret->fd = fd;
+ ret->maxRecordLen = maxRecordLen;
+ ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);
+
+ ret->unconsumed = ret->buffer;
+ ret->read_end = ret->buffer;
+ ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;
+
+ return ret;
+}
+
+
+extern void record_stream_free(RecordStream *rs)
+{
+ free(rs->buffer);
+ free(rs);
+}
+
+
+/* returns NULL; if there isn't a full record in the buffer */
+static unsigned char * getEndOfRecord (unsigned char *p_begin,
+ unsigned char *p_end)
+{
+ size_t len;
+ unsigned char * p_ret;
+
+ if (p_end < p_begin + HEADER_SIZE) {
+ return NULL;
+ }
+
+ //First four bytes are length
+ len = ntohl(*((uint32_t *)p_begin));
+
+ p_ret = p_begin + HEADER_SIZE + len;
+
+ if (p_end < p_ret) {
+ return NULL;
+ }
+
+ return p_ret;
+}
+
+static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)
+{
+ unsigned char *record_start, *record_end;
+
+ record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);
+
+ if (record_end != NULL) {
+ /* one full line in the buffer */
+ record_start = p_rs->unconsumed + HEADER_SIZE;
+ p_rs->unconsumed = record_end;
+
+ *p_outRecordLen = record_end - record_start;
+
+ return record_start;
+ }
+
+ return NULL;
+}
+
+/**
+ * Reads the next record from stream fd
+ * Records are prefixed by a 16-bit big endian length value
+ * Records may not be larger than maxRecordLen
+ *
+ * Doesn't guard against EINTR
+ *
+ * p_outRecord and p_outRecordLen may not be NULL
+ *
+ * Return 0 on success, -1 on fail
+ * Returns 0 with *p_outRecord set to NULL on end of stream
+ * Returns -1 / errno = EAGAIN if it needs to read again
+ */
+int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
+ size_t *p_outRecordLen)
+{
+ void *ret;
+
+ ssize_t countRead;
+
+ /* is there one record already in the buffer? */
+ ret = getNextRecord (p_rs, p_outRecordLen);
+
+ if (ret != NULL) {
+ *p_outRecord = ret;
+ return 0;
+ }
+
+ // if the buffer is full and we don't have a full record
+ if (p_rs->unconsumed == p_rs->buffer
+ && p_rs->read_end == p_rs->buffer_end
+ ) {
+ // this should never happen
+ //LOGE("max record length exceeded\n");
+ assert (0);
+ errno = EFBIG;
+ return -1;
+ }
+
+ if (p_rs->unconsumed != p_rs->buffer) {
+ // move remainder to the beginning of the buffer
+ size_t toMove;
+
+ toMove = p_rs->read_end - p_rs->unconsumed;
+ if (toMove) {
+ memmove(p_rs->buffer, p_rs->unconsumed, toMove);
+ }
+
+ p_rs->read_end = p_rs->buffer + toMove;
+ p_rs->unconsumed = p_rs->buffer;
+ }
+
+ countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);
+
+ if (countRead <= 0) {
+ /* note: end-of-stream drops through here too */
+ *p_outRecord = NULL;
+ return countRead;
+ }
+
+ p_rs->read_end += countRead;
+
+ ret = getNextRecord (p_rs, p_outRecordLen);
+
+ if (ret == NULL) {
+ /* not enough of a buffer to for a whole command */
+ errno = EAGAIN;
+ return -1;
+ }
+
+ *p_outRecord = ret;
+ return 0;
+}
diff --git a/libcutils/selector.c b/libcutils/selector.c
new file mode 100644
index 0000000..9436393
--- /dev/null
+++ b/libcutils/selector.c
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "selector"
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/array.h>
+#include <cutils/selector.h>
+
+#include "loghack.h"
+
+struct Selector {
+ Array* selectableFds;
+ bool looping;
+ fd_set readFds;
+ fd_set writeFds;
+ fd_set exceptFds;
+ int maxFd;
+ int wakeupPipe[2];
+ SelectableFd* wakeupFd;
+
+ bool inSelect;
+ pthread_mutex_t inSelectLock;
+};
+
+/** Reads and ignores wake up data. */
+static void eatWakeupData(SelectableFd* wakeupFd) {
+ static char garbage[64];
+ if (read(wakeupFd->fd, garbage, sizeof(garbage)) < 0) {
+ if (errno == EINTR) {
+ LOGI("read() interrupted.");
+ } else {
+ LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
+ }
+ }
+}
+
+static void setInSelect(Selector* selector, bool inSelect) {
+ pthread_mutex_lock(&selector->inSelectLock);
+ selector->inSelect = inSelect;
+ pthread_mutex_unlock(&selector->inSelectLock);
+}
+
+static bool isInSelect(Selector* selector) {
+ pthread_mutex_lock(&selector->inSelectLock);
+ bool inSelect = selector->inSelect;
+ pthread_mutex_unlock(&selector->inSelectLock);
+ return inSelect;
+}
+
+void selectorWakeUp(Selector* selector) {
+ if (!isInSelect(selector)) {
+ // We only need to write wake-up data if we're blocked in select().
+ return;
+ }
+
+ static char garbage[1];
+ if (write(selector->wakeupPipe[1], garbage, sizeof(garbage)) < 0) {
+ if (errno == EINTR) {
+ LOGI("read() interrupted.");
+ } else {
+ LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno));
+ }
+ }
+}
+
+Selector* selectorCreate(void) {
+ Selector* selector = calloc(1, sizeof(Selector));
+ if (selector == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ selector->selectableFds = arrayCreate();
+
+ // Set up wake-up pipe.
+ if (pipe(selector->wakeupPipe) < 0) {
+ LOG_ALWAYS_FATAL("pipe() error: %s", strerror(errno));
+ }
+
+ LOGD("Wakeup fd: %d", selector->wakeupPipe[0]);
+
+ SelectableFd* wakeupFd = selectorAdd(selector, selector->wakeupPipe[0]);
+ if (wakeupFd == NULL) {
+ LOG_ALWAYS_FATAL("malloc() error.");
+ }
+ wakeupFd->onReadable = &eatWakeupData;
+
+ pthread_mutex_init(&selector->inSelectLock, NULL);
+
+ return selector;
+}
+
+SelectableFd* selectorAdd(Selector* selector, int fd) {
+ assert(selector != NULL);
+
+ SelectableFd* selectableFd = calloc(1, sizeof(SelectableFd));
+ if (selectableFd != NULL) {
+ selectableFd->selector = selector;
+ selectableFd->fd = fd;
+
+ arrayAdd(selector->selectableFds, selectableFd);
+ }
+
+ return selectableFd;
+}
+
+/**
+ * Adds an fd to the given set if the callback is non-null. Returns true
+ * if the fd was added.
+ */
+static inline bool maybeAdd(SelectableFd* selectableFd,
+ void (*callback)(SelectableFd*), fd_set* fdSet) {
+ if (callback != NULL) {
+ FD_SET(selectableFd->fd, fdSet);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Removes stale file descriptors and initializes file descriptor sets.
+ */
+static void prepareForSelect(Selector* selector) {
+ fd_set* exceptFds = &selector->exceptFds;
+ fd_set* readFds = &selector->readFds;
+ fd_set* writeFds = &selector->writeFds;
+
+ FD_ZERO(exceptFds);
+ FD_ZERO(readFds);
+ FD_ZERO(writeFds);
+
+ Array* selectableFds = selector->selectableFds;
+ int i = 0;
+ selector->maxFd = 0;
+ int size = arraySize(selectableFds);
+ while (i < size) {
+ SelectableFd* selectableFd = arrayGet(selectableFds, i);
+ if (selectableFd->remove) {
+ // This descriptor should be removed.
+ arrayRemove(selectableFds, i);
+ size--;
+ if (selectableFd->onRemove != NULL) {
+ selectableFd->onRemove(selectableFd);
+ }
+ free(selectableFd);
+ } else {
+ if (selectableFd->beforeSelect != NULL) {
+ selectableFd->beforeSelect(selectableFd);
+ }
+
+ bool inSet = false;
+ if (maybeAdd(selectableFd, selectableFd->onExcept, exceptFds)) {
+ LOGD("Selecting fd %d for writing...", selectableFd->fd);
+ inSet = true;
+ }
+ if (maybeAdd(selectableFd, selectableFd->onReadable, readFds)) {
+ LOGD("Selecting fd %d for reading...", selectableFd->fd);
+ inSet = true;
+ }
+ if (maybeAdd(selectableFd, selectableFd->onWritable, writeFds)) {
+ inSet = true;
+ }
+
+ if (inSet) {
+ // If the fd is in a set, check it against max.
+ int fd = selectableFd->fd;
+ if (fd > selector->maxFd) {
+ selector->maxFd = fd;
+ }
+ }
+
+ // Move to next descriptor.
+ i++;
+ }
+ }
+}
+
+/**
+ * Invokes a callback if the callback is non-null and the fd is in the given
+ * set.
+ */
+static inline void maybeInvoke(SelectableFd* selectableFd,
+ void (*callback)(SelectableFd*), fd_set* fdSet) {
+ if (callback != NULL && !selectableFd->remove &&
+ FD_ISSET(selectableFd->fd, fdSet)) {
+ LOGD("Selected fd %d.", selectableFd->fd);
+ callback(selectableFd);
+ }
+}
+
+/**
+ * Notifies user if file descriptors are readable or writable, or if
+ * out-of-band data is present.
+ */
+static void fireEvents(Selector* selector) {
+ Array* selectableFds = selector->selectableFds;
+ int size = arraySize(selectableFds);
+ int i;
+ for (i = 0; i < size; i++) {
+ SelectableFd* selectableFd = arrayGet(selectableFds, i);
+ maybeInvoke(selectableFd, selectableFd->onExcept,
+ &selector->exceptFds);
+ maybeInvoke(selectableFd, selectableFd->onReadable,
+ &selector->readFds);
+ maybeInvoke(selectableFd, selectableFd->onWritable,
+ &selector->writeFds);
+ }
+}
+
+void selectorLoop(Selector* selector) {
+ // Make sure we're not already looping.
+ if (selector->looping) {
+ LOG_ALWAYS_FATAL("Already looping.");
+ }
+ selector->looping = true;
+
+ while (true) {
+ setInSelect(selector, true);
+
+ prepareForSelect(selector);
+
+ LOGD("Entering select().");
+
+ // Select file descriptors.
+ int result = select(selector->maxFd + 1, &selector->readFds,
+ &selector->writeFds, &selector->exceptFds, NULL);
+
+ LOGD("Exiting select().");
+
+ setInSelect(selector, false);
+
+ if (result == -1) {
+ // Abort on everything except EINTR.
+ if (errno == EINTR) {
+ LOGI("select() interrupted.");
+ } else {
+ LOG_ALWAYS_FATAL("select() error: %s",
+ strerror(errno));
+ }
+ } else if (result > 0) {
+ fireEvents(selector);
+ }
+ }
+}
diff --git a/libcutils/socket_inaddr_any_server.c b/libcutils/socket_inaddr_any_server.c
new file mode 100644
index 0000000..7d5dab4
--- /dev/null
+++ b/libcutils/socket_inaddr_any_server.c
@@ -0,0 +1,70 @@
+/* libs/cutils/socket_inaddr_any_server.c
+**
+** 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/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#define LISTEN_BACKLOG 4
+
+/* open listen() port on any interface */
+int socket_inaddr_any_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ size_t alen;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
diff --git a/libcutils/socket_local.h b/libcutils/socket_local.h
new file mode 100644
index 0000000..45b9856
--- /dev/null
+++ b/libcutils/socket_local.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef __SOCKET_LOCAL_H
+#define __SOCKET_LOCAL_H
+
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+
+/*
+ * Set up a given sockaddr_un, to have it refer to the given
+ * name in the given namespace. The namespace must be one
+ * of <code>ANDROID_SOCKET_NAMESPACE_ABSTRACT</code>,
+ * <code>ANDROID_SOCKET_NAMESPACE_RESERVED</code>, or
+ * <code>ANDROID_SOCKET_NAMESPACE_FILESYSTEM</code>. Upon success,
+ * the pointed at sockaddr_un is filled in and the pointed at
+ * socklen_t is set to indicate the final length. This function
+ * will fail if the namespace is invalid (not one of the indicated
+ * constants) or if the name is too long.
+ *
+ * @return 0 on success or -1 on failure
+ */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen);
+
+#endif
diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c
new file mode 100644
index 0000000..036ce2e
--- /dev/null
+++ b/libcutils/socket_local_client.c
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#include <cutils/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen)
+{
+ memset (p_addr, 0, sizeof (*p_addr));
+ size_t namelen;
+
+ switch (namespaceId) {
+ case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+ namelen = strlen(name);
+
+ // Test with length +1 for the *initial* '\0'.
+ if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+ goto error;
+ }
+
+ /*
+ * Note: The path in this case is *not* supposed to be
+ * '\0'-terminated. ("man 7 unix" for the gory details.)
+ */
+
+ p_addr->sun_path[0] = 0;
+ memcpy(p_addr->sun_path + 1, name, namelen);
+#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+ /* this OS doesn't have the Linux abstract namespace */
+
+ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_RESERVED:
+ namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+ namelen = strlen(name);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, name);
+ break;
+ default:
+ // invalid namespace id
+ return -1;
+ }
+
+ p_addr->sun_family = AF_LOCAL;
+ *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+int socket_local_client_connect(int fd, const char *name, int namespaceId,
+ int type)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ size_t namelen;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ goto error;
+ }
+
+ if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+ goto error;
+ }
+
+ return fd;
+
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if(s < 0) return -1;
+
+ if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c
new file mode 100644
index 0000000..4971b1b
--- /dev/null
+++ b/libcutils/socket_local_server.c
@@ -0,0 +1,124 @@
+/* libs/cutils/socket_local_server.c
+**
+** 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/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+
+/**
+ * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
+ * returns 's' on success, -1 on fail
+ *
+ * Does not call listen()
+ */
+int socket_local_server_bind(int s, const char *name, int namespaceId)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int n;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ return -1;
+ }
+
+ /* basically: if this is a filesystem path, unlink first */
+#ifndef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+ if (1) {
+#else
+ if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
+ || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
+#endif
+ /*ignore ENOENT*/
+ unlink(addr.sun_path);
+ }
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
+ return -1;
+ }
+
+ return s;
+
+}
+
+
+/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem
+ * namespace
+ *
+ * Returns fd on success, -1 on fail
+ */
+
+int socket_local_server(const char *name, int namespace, int type)
+{
+ int err;
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if (s < 0) return -1;
+
+ err = socket_local_server_bind(s, name, namespace);
+
+ if (err < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c
new file mode 100644
index 0000000..cb82c5e
--- /dev/null
+++ b/libcutils/socket_loopback_client.c
@@ -0,0 +1,59 @@
+/* libs/cutils/socket_loopback_client.c
+**
+** 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/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+/* Connect to port on the loopback IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM.
+ * return is a file descriptor or -1 on error
+ */
+int socket_loopback_client(int port, int type)
+{
+ struct sockaddr_in addr;
+ socklen_t alen;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
diff --git a/libcutils/socket_loopback_server.c b/libcutils/socket_loopback_server.c
new file mode 100644
index 0000000..3208488
--- /dev/null
+++ b/libcutils/socket_loopback_server.c
@@ -0,0 +1,71 @@
+/* libs/cutils/socket_loopback_server.c
+**
+** 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/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#define LISTEN_BACKLOG 4
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+/* open listen() port on loopback interface */
+int socket_loopback_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ size_t alen;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c
new file mode 100644
index 0000000..a64006c
--- /dev/null
+++ b/libcutils/socket_network_client.c
@@ -0,0 +1,65 @@
+/* libs/cutils/socket_network_client.c
+**
+** 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/sockets.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+
+
+/* Connect to port on the IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM.
+ * return is a file descriptor or -1 on error
+ */
+int socket_network_client(const char *host, int port, int type)
+{
+ struct hostent *hp;
+ struct sockaddr_in addr;
+ socklen_t alen;
+ int s;
+
+ hp = gethostbyname(host);
+ if(hp == 0) return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ s = socket(hp->h_addrtype, type, 0);
+ if(s < 0) return -1;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
new file mode 100644
index 0000000..fadaabe
--- /dev/null
+++ b/libcutils/strdup16to8.c
@@ -0,0 +1,104 @@
+/* libs/cutils/strdup16to8.c
+**
+** 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/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+
+
+/**
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
+{
+ size_t utf8Len = 0;
+
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff)
+ utf8Len += 3;
+ else if (uic > 0x7f || uic == 0)
+ utf8Len += 2;
+ else
+ utf8Len++;
+ }
+ return utf8Len;
+}
+
+
+/**
+ * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
+ *
+ * This basically means: embedded \0's in the UTF-16 string are encoded
+ * as "0xc0 0x80"
+ *
+ * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
+ * not just "len".
+ *
+ * Please note, a terminated \0 is always added, so your result will always
+ * be "strlen16to8() + 1" bytes long.
+ */
+extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
+{
+ char* utf8cur = utf8Str;
+
+ while (len--) {
+ unsigned int uic = *utf16Str++;
+
+ if (uic > 0x07ff) {
+ *utf8cur++ = (uic >> 12) | 0xe0;
+ *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else if (uic > 0x7f || uic == 0) {
+ *utf8cur++ = (uic >> 6) | 0xc0;
+ *utf8cur++ = (uic & 0x3f) | 0x80;
+ } else {
+ *utf8cur++ = uic;
+
+ if (uic == 0) {
+ break;
+ }
+ }
+ }
+
+ *utf8cur = '\0';
+
+ return utf8Str;
+}
+
+/**
+ * Convert a UTF-16 string to UTF-8.
+ *
+ * Make sure you allocate "dest" with the result of strblen16to8(),
+ * not just "strlen16()".
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+ char *ret;
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ ret = malloc(strnlen16to8(s, n) + 1);
+
+ strncpy16to8 (ret, s, n);
+
+ return ret;
+}
diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c
new file mode 100644
index 0000000..8654b04
--- /dev/null
+++ b/libcutils/strdup8to16.c
@@ -0,0 +1,209 @@
+/* libs/cutils/strdup8to16.c
+**
+** 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/jstring.h>
+#include <assert.h>
+#include <stdlib.h>
+
+/* See http://www.unicode.org/reports/tr22/ for discussion
+ * on invalid sequences
+ */
+
+#define UTF16_REPLACEMENT_CHAR 0xfffd
+
+/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
+#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1)
+
+/* note: macro expands to multiple lines */
+#define UTF8_SHIFT_AND_MASK(unicode, byte) \
+ (unicode)<<=6; (unicode) |= (0x3f & (byte));
+
+#define UNICODE_UPPER_LIMIT 0x10fffd
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len)
+{
+ char16_t *ret;
+ size_t len;
+
+ if (s == NULL) return NULL;
+
+ len = strlen8to16(s);
+
+ // no plus-one here. UTF-16 strings are not null terminated
+ ret = (char16_t *) malloc (sizeof(char16_t) * len);
+
+ return strcpy8to16 (ret, s, out_len);
+}
+
+/**
+ * Like "strlen", but for strings encoded with Java's modified UTF-8.
+ *
+ * The value returned is the number of UTF-16 characters required
+ * to represent this string.
+ */
+extern size_t strlen8to16 (const char* utf8Str)
+{
+ size_t len = 0;
+ int ic;
+ int expected = 0;
+
+ while ((ic = *utf8Str++) != '\0') {
+ /* bytes that start 0? or 11 are lead bytes and count as characters.*/
+ /* bytes that start 10 are extention bytes and are not counted */
+
+ if ((ic & 0xc0) == 0x80) {
+ /* count the 0x80 extention bytes. if we have more than
+ * expected, then start counting them because strcpy8to16
+ * will insert UTF16_REPLACEMENT_CHAR's
+ */
+ expected--;
+ if (expected < 0) {
+ len++;
+ }
+ } else {
+ len++;
+ expected = UTF8_SEQ_LENGTH(ic) - 1;
+
+ /* this will result in a surrogate pair */
+ if (expected == 3) {
+ len++;
+ }
+ }
+ }
+
+ return len;
+}
+
+
+
+/*
+ * Retrieve the next UTF-32 character from a UTF-8 string.
+ *
+ * Stops at inner \0's
+ *
+ * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ */
+static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
+{
+ uint32_t ret;
+ int seq_len;
+ int i;
+
+ /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
+ static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+
+ /* Bytes that start with bits "10" are not leading characters. */
+ if (((**pUtf8Ptr) & 0xc0) == 0x80) {
+ (*pUtf8Ptr)++;
+ return UTF16_REPLACEMENT_CHAR;
+ }
+
+ /* note we tolerate invalid leader 11111xxx here */
+ seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
+
+ ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
+
+ if (**pUtf8Ptr == '\0') return ret;
+
+ (*pUtf8Ptr)++;
+ for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
+ if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
+ if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
+
+ UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
+ }
+
+ return ret;
+}
+
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str,
+ size_t *out_len)
+{
+ char16_t *dest = utf16Str;
+
+ while (*utf8Str != '\0') {
+ uint32_t ret;
+
+ ret = getUtf32FromUtf8(&utf8Str);
+
+ if (ret <= 0xffff) {
+ *dest++ = (char16_t) ret;
+ } else if (ret <= UNICODE_UPPER_LIMIT) {
+ /* Create surrogate pairs */
+ /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+ *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+ *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff);
+ } else {
+ *dest++ = UTF16_REPLACEMENT_CHAR;
+ }
+ }
+
+ *out_len = dest - utf16Str;
+
+ return utf16Str;
+}
+
+/**
+ * length is the number of characters in the UTF-8 string.
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
+ int length, size_t *out_len)
+{
+ /* TODO: Share more of this code with the method above. Only 2 lines changed. */
+
+ char16_t *dest = utf16Str;
+
+ const char *end = utf8Str + length; /* This line */
+ while (utf8Str < end) { /* and this line changed. */
+ uint32_t ret;
+
+ ret = getUtf32FromUtf8(&utf8Str);
+
+ if (ret <= 0xffff) {
+ *dest++ = (char16_t) ret;
+ } else if (ret <= UNICODE_UPPER_LIMIT) {
+ /* Create surrogate pairs */
+ /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+ *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+ *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff);
+ } else {
+ *dest++ = UTF16_REPLACEMENT_CHAR;
+ }
+ }
+
+ *out_len = dest - utf16Str;
+
+ return utf16Str;
+}
diff --git a/libcutils/threads.c b/libcutils/threads.c
new file mode 100644
index 0000000..42cc928
--- /dev/null
+++ b/libcutils/threads.c
@@ -0,0 +1,84 @@
+/* libs/cutils/threads.c
+**
+** 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/threads.h>
+
+#ifdef HAVE_PTHREADS
+void* thread_store_get( thread_store_t* store )
+{
+ const pthread_key_t k = store->tls;
+
+ if (!store->has_tls)
+ return NULL;
+
+ return pthread_getspecific( store->tls );
+}
+
+extern void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy)
+{
+ pthread_mutex_lock( &store->lock );
+ if (!store->has_tls) {
+ if (pthread_key_create( &store->tls, destroy) != 0) {
+ pthread_mutex_unlock(&store->lock);
+ return;
+ }
+ store->has_tls = 1;
+ }
+ pthread_mutex_unlock( &store->lock );
+
+ pthread_setspecific( store->tls, value );
+}
+
+#endif
+
+#ifdef HAVE_WIN32_THREADS
+void* thread_store_get( thread_store_t* store )
+{
+ if (!store->has_tls)
+ return NULL;
+
+ return (void*) TlsGetValue( store->tls );
+}
+
+void thread_store_set( thread_store_t* store,
+ void* value,
+ thread_store_destruct_t destroy )
+{
+ /* XXX: can't use destructor on thread exit */
+ if (!store->lock_init) {
+ store->lock_init = -1;
+ InitializeCriticalSection( &store->lock );
+ store->lock_init = -2;
+ } else while (store->lock_init != -2) {
+ Sleep(10); /* 10ms */
+ }
+
+ EnterCriticalSection( &store->lock );
+ if (!store->has_tls) {
+ store->tls = TlsAlloc();
+ if (store->tls == TLS_OUT_OF_INDEXES) {
+ LeaveCriticalSection( &store->lock );
+ return;
+ }
+ store->has_tls = 1;
+ }
+ LeaveCriticalSection( &store->lock );
+
+ TlsSetValue( store->tls, value );
+}
+#endif
diff --git a/libcutils/tzfile.h b/libcutils/tzfile.h
new file mode 100644
index 0000000..8c70375
--- /dev/null
+++ b/libcutils/tzfile.h
@@ -0,0 +1,180 @@
+#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/tztime.c b/libcutils/tztime.c
new file mode 100644
index 0000000..93bbb29
--- /dev/null
+++ b/libcutils/tztime.c
@@ -0,0 +1,1915 @@
+/*
+** 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 */
+
+#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, 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;
+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;
+{
+ int number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int
+long_increment_overflow(number, delta)
+long * number;
+int delta;
+{
+ long number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+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) {
+ ++t;
+ if (t <= lo)
+ return WRONG;
+ ++lo;
+ } else if (t == hi) {
+ --t;
+ if (t >= hi)
+ return WRONG;
+ --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/uio.c b/libcutils/uio.c
new file mode 100644
index 0000000..baa8051
--- /dev/null
+++ b/libcutils/uio.c
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef HAVE_SYS_UIO_H
+
+#include <cutils/uio.h>
+#include <unistd.h>
+
+int readv( int fd, struct iovec* vecs, int count )
+{
+ int total = 0;
+
+ for ( ; count > 0; count--, vecs++ ) {
+ const char* buf = vecs->iov_base;
+ int len = vecs->iov_len;
+
+ while (len > 0) {
+ int ret = read( fd, buf, len );
+ if (ret < 0) {
+ if (total == 0)
+ total = -1;
+ goto Exit;
+ }
+ if (ret == 0)
+ goto Exit;
+
+ total += ret;
+ buf += ret;
+ len -= ret;
+ }
+ }
+Exit:
+ return total;
+}
+
+int writev( int fd, const struct iovec* vecs, int count )
+{
+ int total = 0;
+
+ for ( ; count > 0; count--, vecs++ ) {
+ const char* buf = (const char*)vecs->iov_base;
+ int len = (int)vecs->iov_len;
+
+ while (len > 0) {
+ int ret = write( fd, buf, len );
+ if (ret < 0) {
+ if (total == 0)
+ total = -1;
+ goto Exit;
+ }
+ if (ret == 0)
+ goto Exit;
+
+ total += ret;
+ buf += ret;
+ len -= ret;
+ }
+ }
+Exit:
+ return total;
+}
+
+#endif /* !HAVE_SYS_UIO_H */
diff --git a/libcutils/zygote.c b/libcutils/zygote.c
new file mode 100644
index 0000000..aa060c0
--- /dev/null
+++ b/libcutils/zygote.c
@@ -0,0 +1,267 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Zygote"
+
+#include <cutils/sockets.h>
+#include <cutils/zygote.h>
+#include <cutils/log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define ZYGOTE_SOCKET "zygote"
+
+#define ZYGOTE_RETRY_COUNT 1000
+#define ZYGOTE_RETRY_MILLIS 500
+
+static void replace_nl(char *str);
+
+/*
+ * If sendStdio is non-zero, the current process's stdio file descriptors
+ * will be sent and inherited by the spawned process.
+ */
+static int send_request(int fd, int sendStdio, int argc, const char **argv)
+{
+#ifndef HAVE_ANDROID_OS
+ // not supported on simulator targets
+ //LOGE("zygote_* not supported on simulator targets");
+ return -1;
+#else /* HAVE_ANDROID_OS */
+ uint32_t pid;
+ int i;
+ struct iovec ivs[2];
+ struct msghdr msg;
+ char argc_buffer[12];
+ const char *newline_string = "\n";
+ struct cmsghdr *cmsg;
+ char msgbuf[CMSG_SPACE(sizeof(int) * 3)];
+ int *cmsg_payload;
+ ssize_t ret;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&ivs, 0, sizeof(ivs));
+
+ // First line is arg count
+ snprintf(argc_buffer, sizeof(argc_buffer), "%d\n", argc);
+
+ ivs[0].iov_base = argc_buffer;
+ ivs[0].iov_len = strlen(argc_buffer);
+
+ msg.msg_iov = ivs;
+ msg.msg_iovlen = 1;
+
+ if (sendStdio != 0) {
+ // Pass the file descriptors with the first write
+ msg.msg_control = msgbuf;
+ msg.msg_controllen = sizeof msgbuf;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+
+ cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ cmsg_payload = (int *)CMSG_DATA(cmsg);
+ cmsg_payload[0] = STDIN_FILENO;
+ cmsg_payload[1] = STDOUT_FILENO;
+ cmsg_payload[2] = STDERR_FILENO;
+ }
+
+ do {
+ ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ // Only send the fd's once
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+ // replace any newlines with spaces and send the args
+ for (i = 0; i < argc; i++) {
+ char *tofree = NULL;
+ const char *toprint;
+
+ toprint = argv[i];
+
+ if (strchr(toprint, '\n') != NULL) {
+ tofree = strdup(toprint);
+ toprint = tofree;
+ replace_nl(tofree);
+ }
+
+ ivs[0].iov_base = (char *)toprint;
+ ivs[0].iov_len = strlen(toprint);
+ ivs[1].iov_base = (char *)newline_string;
+ ivs[1].iov_len = 1;
+
+ msg.msg_iovlen = 2;
+
+ do {
+ ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
+ } while (ret < 0 && errno == EINTR);
+
+ if (tofree != NULL) {
+ free(tofree);
+ }
+
+ if (ret < 0) {
+ return -1;
+ }
+ }
+
+ // Read the pid, as a 4-byte network-order integer
+
+ ivs[0].iov_base = &pid;
+ ivs[0].iov_len = sizeof(pid);
+ msg.msg_iovlen = 1;
+
+ do {
+ do {
+ ret = recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_WAITALL);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ return -1;
+ }
+
+ ivs[0].iov_len -= ret;
+ ivs[0].iov_base += ret;
+ } while (ivs[0].iov_len > 0);
+
+ pid = ntohl(pid);
+
+ return pid;
+#endif /* HAVE_ANDROID_OS */
+}
+
+int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int))
+{
+ int fd;
+ int pid;
+ int err;
+ const char *newargv[argc + 1];
+
+ fd = socket_local_client(ZYGOTE_SOCKET,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, AF_LOCAL);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ // The command socket is passed to the peer as close-on-exec
+ // and will close when the peer dies
+ newargv[0] = "--peer-wait";
+ memcpy(newargv + 1, argv, argc * sizeof(*argv));
+
+ pid = send_request(fd, 1, argc + 1, newargv);
+
+ if (pid > 0 && post_run_func != NULL) {
+ post_run_func(pid);
+ }
+
+ // Wait for socket to close
+ do {
+ int dummy;
+ err = read(fd, &dummy, sizeof(dummy));
+ } while ((err < 0 && errno == EINTR) || err != 0);
+
+ do {
+ err = close(fd);
+ } while (err < 0 && errno == EINTR);
+
+ return 0;
+}
+
+/**
+ * Spawns a new dalvik instance via the Zygote process. The non-zygote
+ * arguments are passed to com.android.internal.os.RuntimeInit(). The
+ * first non-option argument should be a class name in the system class path.
+ *
+ * The arg list may start with zygote params such as --set-uid.
+ *
+ * If sendStdio is non-zero, the current process's stdio file descriptors
+ * will be sent and inherited by the spawned process.
+ *
+ * The pid of the child process is returned, or -1 if an error was
+ * encountered.
+ *
+ * zygote_run_oneshot waits up to ZYGOTE_RETRY_COUNT *
+ * ZYGOTE_RETRY_MILLIS for the zygote socket to be available.
+ */
+int zygote_run_oneshot(int sendStdio, int argc, const char **argv)
+{
+ int fd = -1;
+ int err;
+ int i;
+ int retries;
+ int pid;
+ const char **newargv = argv;
+ const int newargc = argc;
+
+ for (retries = 0; (fd < 0) && (retries < ZYGOTE_RETRY_COUNT); retries++) {
+ if (retries > 0) {
+ struct timespec ts;
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tv_nsec = ZYGOTE_RETRY_MILLIS * 1000 * 1000;
+
+ do {
+ err = nanosleep (&ts, &ts);
+ } while (err < 0 && errno == EINTR);
+ }
+ fd = socket_local_client(ZYGOTE_SOCKET, AF_LOCAL,
+ ANDROID_SOCKET_NAMESPACE_RESERVED);
+ }
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ pid = send_request(fd, 0, newargc, newargv);
+
+ do {
+ err = close(fd);
+ } while (err < 0 && errno == EINTR);
+
+ return pid;
+}
+
+/**
+ * Replaces all occurrances of newline with space.
+ */
+static void replace_nl(char *str)
+{
+ for(; *str; str++) {
+ if (*str == '\n') {
+ *str = ' ';
+ }
+ }
+}
+
+
+