diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53 (patch) | |
tree | 54fd1b2695a591d2306d41264df67c53077b752c /libcutils | |
download | system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.zip system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.gz system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.bz2 |
Initial Contribution
Diffstat (limited to 'libcutils')
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 = ¤t->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 = ¤t->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 = ¤t->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 = ' '; + } + } +} + + + |